我想通过x
类型变量创建重载函数,它可以统一这两种类型:
append :: A -> x -> A
append :: MonadThrow m => A -> x -> m A
在这种情况下,我应该写什么类型的课程?目前我有这样的事情:
class MyClasss (x :: *) (m :: * -> *) | x -> m where
type MonadConstraint x :: (* -> *) -> Constraint
append :: MonadConstraint x m => A -> x -> m A
我可以使用
Identity
monad模拟纯版本并创建帮助函数。
但是当我创建像这样的实例时:
instance MyClass X m where ...
我看到以下编译错误:
• Illegal instance declaration for 'MyClass Text m'
The coverage condition fails in class 'MyClass'
for functional dependency: 'p -> m'
Reason: lhs type 'X' does not determine rhs type 'm'
Un-determined variable: m
• In the instance declaration for 'MyClass X m'
|
94 | instance MyClass X m where
| ^^^^^^^^^^^
我可以做些什么来抽象这两个函数?
答案 0 :(得分:2)
我不确定我是否推荐这个,但它编译并运行,至少对于给定的情况。
{-# LANGUAGE TypeFamilies, FlexibleInstances, MultiParamTypeClasses,
AllowAmbiguousTypes #-}
import Control.Monad.IO.Class
data A = A Int deriving Show
data X1 = X1
data X2 = X2
我们使返回类型由类型函数表示。
class MyClass (x :: *) (m :: * -> *) where
type Ret x m :: *
append :: A -> x -> Ret x m
如果X1
没有导致monadic结果,我们会Ret X1 m
非monadic。
instance MyClass X1 m where
type Ret X1 m = A
-- append :: A -> X1 -> A
append a x = a
否则,我们将Ret X1 m
monadic。
instance MonadIO m => MyClass X2 m where
type Ret X2 m = m A
-- append :: MonadIO m => A -> X2 -> m A
append a x = liftIO (putStrLn "hi!") >> return a
缺点是这涉及不明确的类型,因此我们必须打开该扩展。但是,以下测试通过正常,因为GHC能够解决歧义。
test1 :: A
test1 = append (A 3) X1
上面,没有任何内容告诉GHC m
是什么,但由于X1
只有一个实例,因此不会出现错误。
test2 :: IO A
test2 = append (A 3) X2
同样为X2
。