Haskell中shouldBeRight与shouldBeJust函数编译报错原因咨询
解决Either/Maybe取值函数的编译歧义问题
你写的这两个函数逻辑完全没问题,但编译报错是因为类型歧义导致的,我来拆解一下具体原因和修复方法:
错误原因分析
throwIO函数的类型是Exception e => e -> IO a,它要求传入的参数必须是某个实现了Exception类型类的值。而你直接传入字符串字面量(比如"should be Just value but was Nothing")时,Haskell编译器没办法确定这个字符串应该被解析成哪个具体的异常类型——因为有很多类型同时实现了Exception和IsString(字符串字面量自动转换),比如String、IOException等等,编译器找不到唯一的匹配类型,就会抛出歧义错误。
修复方案
有几种简单的方式可以解决这个歧义:
方案1:使用userError构造具体异常
userError会直接生成一个IOError类型的异常,它属于Exception的实例,编译器能明确识别:
import Control.Exception (throwIO, userError) shouldBeRight :: (Show a) => Either a b -> IO b shouldBeRight (Left a) = throwIO $ userError $ concat ["should be Right value but wasn't, because:", show a] shouldBeRight (Right b) = return b shouldBeJust :: Maybe a -> IO a shouldBeJust Nothing = throwIO $ userError "should be Just value but was Nothing" shouldBeJust (Just a) = return a
方案2:给字符串字面量加类型注解
直接指定字符串对应的异常类型,比如IOException:
import Control.Exception (throwIO, IOException) shouldBeRight :: (Show a) => Either a b -> IO b shouldBeRight (Left a) = throwIO $ (concat ["should be Right value but wasn't, because:", show a] :: IOException) shouldBeRight (Right b) = return b shouldBeJust :: Maybe a -> IO a shouldBeJust Nothing = throwIO ("should be Just value but was Nothing" :: IOException) shouldBeJust (Just a) = return a
方案3:使用error配合liftIO(适用于MonadIO上下文)
如果你的代码是在支持MonadIO的上下文中,也可以先用error生成异常再用liftIO转换,但这种方式不如前两种直接:
import Control.Monad.IO.Class (liftIO) shouldBeJust :: Maybe a -> IO a shouldBeJust Nothing = liftIO $ error "should be Just value but was Nothing" shouldBeJust (Just a) = return a
验证修复
上面的任意一种方案都能让编译器明确异常类型,消除歧义,你的函数就能正常编译运行了。当遇到Left或Nothing时,函数会抛出明确的异常信息,完全符合你预期的行为。
内容的提问来源于stack exchange,提问作者Leo Zhang
相关产品推荐
相关产品推荐

