Haskell 为什么可以';延续monad不存在MonadFix的实例吗?
我们怎样才能证明它没有有效的实例呢?事实上,这并不是说不可能有一个Haskell 为什么可以';延续monad不存在MonadFix的实例吗?,haskell,monads,continuation,monadfix,Haskell,Monads,Continuation,Monadfix,我们怎样才能证明它没有有效的实例呢?事实上,这并不是说不可能有一个MonadFix实例,只是库的类型有点太受限了。如果您在所有可能的rs上定义ContT,那么不仅MonadFix成为可能,而且在Monad之前的所有实例都不需要基础函子: newtype ContT m a = ContT { runContT :: forall r. (a -> m r) -> m r } instance Functor (ContT m) where fmap f (ContT k) = C
MonadFix
实例,只是库的类型有点太受限了。如果您在所有可能的r
s上定义ContT
,那么不仅MonadFix
成为可能,而且在Monad
之前的所有实例都不需要基础函子:
newtype ContT m a = ContT { runContT :: forall r. (a -> m r) -> m r }
instance Functor (ContT m) where
fmap f (ContT k) = ContT (\kb -> k (kb . f))
instance Monad (ContT m) where
return a = ContT ($a)
join (ContT kk) = ContT (\ka -> kk (\(ContT k) -> k ka))
instance MonadFix m => MonadFix (ContT m) where
mfix f = ContT (\ka -> mfixing (\a -> runContT (f a) ka<&>(,a)))
where mfixing f = fst <$> mfix (\ ~(_,a) -> f a )
newtypeconttma=ContT{runContT::forall r.(a->mr)->mr}
实例函子(ContT m),其中
fmap f(ContT k)=ContT(\kb->k(kb.f))
实例Monad(ContT m)其中
返回a=ContT($a)
join(conttkk)=ContT(\ka->kk(\(conttk)->kka))
实例MonadFix m=>MonadFix(ContT m),其中
mfix f=ContT(\ka->mfix(\a->runContT(fa)ka(,a)))
式中,mfixing f=fst mfix(\~(\~,a)->f a)
考虑延续monad的mfix
类型签名
(a->ContT r m a)->ContT r m a
--展开新类型
(a->(a->mr)->mr)->(a->mr)->mr
这里有证据证明没有这种类型的纯粹居民
---------------------------------------------
(a->(a->mr)->mr)->(a->mr)->mr
介绍f,k
f::a->(a->mr)->mr
k::a->m r
---------------------------
m r
应用k
f::a->(a->mr)->mr
k::a->m r
---------------------------
A.
死路
f::a->(a->mr)->mr
k::a->m r
---------------------------
m r
申请f
f::a->(a->m r)->m r f::a->(a->m r)->m r
k::a->m r k::a->m r
--------------------------- ---------------------------
a->m r
死端自反性
正如您所看到的,问题在于f
和k
都希望输入类型为a
的值。但是,没有办法变出a
类型的值。因此,对于延续单子,mfix
没有纯粹的居住者
请注意,您不能递归地定义mfix
,因为mfix f k=mfix代码>将导致无限回归,因为没有基本情况。而且,我们不能定义mfix fk=f
或mfix f k=k?
因为即使使用递归,也无法变出a
类型的值
但是,我们是否可以为延续monad使用不纯净的mfix
?请考虑以下内容:
import Control.Concurrent.MVar
进口管制
导入控制.Monad.Fix
导入System.IO不安全
实例MonadFix(ContT r m)其中
mfix f=ContT$\k->unsafePerformIO$do
m ContT r m a)->ContT r m a
常数mfix=f=>k=>{
常数ys=[];
返回(函数迭代(n){
设i=0,x;
返回f(()=>{
如果(i>n)返回x;
抛出新引用错误(“未定义x”);
})(y=>{
常数j=i++;
如果(j==n){
ys[j]=k(x=y);
迭代(i);
}
返回ys[j];
});
}(0));
};
常数示例=triple=>k=>[
{a:()=>1,b:()=>2,c:()=>triple().a()+triple().b()},
{a:()=>2,b:()=>triple().c()-triple().a(),c:()=>5},
{a:()=>triple().c()-triple().b(),b:()=>5,c:()=>8},
].flatMap(k);
const result=mfix(示例)(({a,b,c})=>[{a:a(),b:b(),c:c()}]);
控制台日志(结果)代码>看起来您的类型实际上是更受约束的类型。是否存在将参数强制为ContT
多态会阻止有用实现的实际情况?如果不是,这可能只是一个历史问题-ContT
已经存在很长时间了,很可能是在排名2的类型成为Haskell公认的一部分之前。多态参数ContT
也称为Codensity
。它缺乏定义callCC
的能力。这个答案解释了为什么您的用于all r。(a->m r)->m r
ContT
不能有callCC
。好吧,我确实不能用Codensity
(谢谢你,厄尔詹,教我一个新词:-)),来定义Control.Monad.Cont.callCC
,但是如果我们使用一个看起来像Scheme延续的typeclass,实例几乎会自己写:类MonadCont m where callCC:(对于所有b.(a->mb)->m b)->m a
。我们可以以一种更为一致的方式使用这个实例,即我们不直接在延拓中获取值,而是使用我们生成的值运行计算的其余部分,我们还不知道该值的类型(因此,forall
).该证明在这种特殊情况下不具有说服力,因为它只考虑实现而不考虑递归,而递归正是MonadFix
的关键所在。ContT
的MonadFix
实例打破了引用的透明性:x
的值取决于是否调用延续,这取决于求值顺序,即使它最多应用一次。另一方面,如果你接受不安全,这可能是一种有趣的打结方式。@李耀霞你也不能递归地定义mfix
,因为mfix f k=mfix代码>将导致无限回归,因为没有基本情况。而且,我们不能定义mfix fk=f
或mfix f k=k?
因为即使使用递归,也无法变出a类型的值
@liyaoxia True。它确实破坏了引用的透明度。