在本例中,绑定操作符的Haskell定义有什么问题?
我正在学习蒙纳德变形金刚教程 ,它要求我尝试为在本例中,绑定操作符的Haskell定义有什么问题?,haskell,monads,ghc,ghci,Haskell,Monads,Ghc,Ghci,我正在学习蒙纳德变形金刚教程 ,它要求我尝试为EitherIO数据类型实现Monad实例,定义为: data EitherIO e a = EitherIO { runEitherIO :: IO (Either e a) } join x = x >>= id 所以我试着: instance Functor (EitherIO e) where fmap f = EitherIO . fmap (fmap f) . runEitherIO instance Mon
EitherIO
数据类型实现Monad实例,定义为:
data EitherIO e a = EitherIO {
runEitherIO :: IO (Either e a)
}
join x = x >>= id
所以我试着:
instance Functor (EitherIO e) where
fmap f = EitherIO . fmap (fmap f) . runEitherIO
instance Monad (EitherIO e) where
return = EitherIO . return . Right
x >>= f = join $ fmap f x
本教程的版本有点不同:
instance Monad (EitherIO e) where
return = pure -- the same as EitherIO . return . Right
x >>= f = EitherIO $ runEitherIO x >>= either (return . Left) (runEitherIO . f)
现在,所有类型都适合,所以我认为我很好,并祝贺自己终于找到了一个使用
结果,我被要求运行runEitherIO getToken
,运行以下命令:
liftEither x = EitherIO (return x)
liftIO x = EitherIO (fmap Right x)
getToken = do
liftIO (T.putStrLn "Enter email address:")
input <- liftIO T.getLine
liftEither (getDomain input)
liftEither x=EitherIO(返回x)
liftIO x=EitherIO(fmap右x)
getToken=do
liftIO(T.putStrLn“输入电子邮件地址:”)
输入>=
运算符,在我提供一些输入后,GHCi将挂起。即使在我通过^C
中断之后,GHCi也会开始表现出奇怪的行为,在我打字时会挂上一两秒钟。我必须杀了GHCi才能重新开始。当我用教程定义替换>=
定义时,一切正常
我的完整档案是
因此:
我猜这就是罪魁祸首:
instance Monad (EitherIO e) where
return = EitherIO . return . Right
x >>= f = join $ fmap f x
但是,join
定义为:
data EitherIO e a = EitherIO {
runEitherIO :: IO (Either e a)
}
join x = x >>= id
但是,通过这种方式,join
和>=
以一种相互递归的方式定义,从而导致不终止
请注意,此类型将进行检查,如下所示:
f, g :: Int -> Int
f x = g x
g x = f x
底线:您应该为>=
提供一个定义,该定义不涉及控件中的连接
join :: (Monad m) => m (m a) -> m a
join x = x >>= id
你看,join
是根据>=
定义的。因此,简单的回答是,您对>=
的定义进入了一个无休止的循环(即不终止),因为它使用了连接
,而连接又使用了>=
如果您仔细想想,您可以对每个单子使用>=
的定义。所以它不可能工作,因为它根本没有使用类型的内部结构
至于为什么ghc没有检测到这一点,这是一个无休止的循环,但不是类型错误。Haskell的类型系统不足以检测此类循环。您实际上无法定义join
,它不是该类的方法。有计划将其纳入AMP提案中,但它与新类型派生的角色系统的交互作用不好。(基本上,这会使单子堆栈的新类型派生变得不可能。)我明白了。。。我想没有一个可用的函数列表被标记为禁止在Monad定义中使用?@Ana没有,AFAIK。经验法则是,您不应该使用任何需要Monad(EitherIO e)
来定义实例本身的函数。(在某些情况下,此循环定义仍可能导致终止,但通常情况下并非如此。)