Haskell syb库mkM使用方法及传入自定义函数报错问题求解
错误原因分析
printMyInt传入mkM报错的核心原因是类型不匹配:mkM要求传入的函数签名为b -> m b,即输入类型和Monad包裹的输出类型必须一致,都为b。而你定义的printMyInt签名是MyInt -> IO (),输出的Monad包裹值类型是(),和输入的MyInt不匹配,GHC因此抛出类型匹配错误。pure传入mkM报错是因为类型推导歧义:pure本身是多态函数,签名为Applicative f => a -> f a,没有显式指定类型的情况下,GHC无法确定你要匹配的b具体是什么类型,自然无法满足Typeable b的约束,因此抛出实例缺失错误。
正确使用mkM的方式
1. 修正函数签名适配mkM要求
如果需要在遍历过程中打印值同时保留原值,可以修改打印函数返回Monad包裹的输入值:
printMyInt' :: MyInt -> IO MyInt printMyInt' mi@(MyInt i) = do putStrLn $ "MyInt = " ++ show i pure mi
此时查询类型即可正常通过:
> :t mkM printMyInt' mkM printMyInt' :: (MonadIO m, Typeable a) => a -> m a
2. 多态函数显式指定类型
如果要使用pure这类多态函数,可以通过类型签名或者类型应用固定要匹配的类型:
-- 方式1:给pure加签名固定类型 > :t mkM (pure :: MyInt -> IO MyInt) mkM (pure :: MyInt -> IO MyInt) :: Typeable a => a -> IO a -- 方式2:开启TypeApplications扩展直接指定类型参数 > :set -XTypeApplications > :t mkM @MyInt pure mkM @MyInt pure :: (Monad m, Typeable a) => a -> m a
3. 配合遍历函数使用示例
mkM通常和everywhereM等遍历函数配合使用,处理嵌套数据结构:
-- 定义包含MyInt的嵌套结构 data Foo = Foo MyInt [MyInt] deriving (Show, Data) testFoo = Foo (MyInt 1) [MyInt 2, MyInt 3] -- 遍历打印所有MyInt值 > everywhereM (mkM printMyInt') testFoo MyInt = 1 MyInt = 2 MyInt = 3 Foo (MyInt 1) [MyInt 2,MyInt 3]
内容的提问来源于stack exchange,提问作者Daniil Iaitskov
相关产品推荐
相关产品推荐

