我如何编写一个通用函数run
,该函数采用某个monad转换器的对象并调用相应的函数?
给出run s
,
s
是StateT
,则run = runStateT
s
是ReaderT
,则run = runReaderT
s
是MaybeT
,则run = runMaybeT
我尝试创建类型类Runnable
:
:set -XMultiParamTypeClasses
:set -XFlexibleInstances
class Runnable a b where
run :: a -> b
(//) :: a -> b
(//) = run
instance Runnable (StateT s m a) (s -> m (a, s)) where
run = runStateT
instance Runnable (ReaderT r m a) (r -> m a) where
run = runReaderT
但是当我尝试使用run
时,它不起作用。例如,让我们定义simpleReader
,它在读取时仅返回10
:
simpleReader = ReaderT $ \env -> Just 10
runReaderT simpleReader ()
此输出Just 10
,如预期的那样。
但是,当我尝试使用run
时,它给了我一个错误:
run simpleReader ()
<interactive>:1:1: error:
• Non type-variable argument in the constraint: Runnable (ReaderT r Maybe a) (() -> t)
(Use FlexibleContexts to permit this)
• When checking the inferred type
it :: forall r a t. (Runnable (ReaderT r Maybe a) (() -> t), Num a) => t
如果按照建议的方式启用FlexibleContexts
,则会收到其他错误:
<interactive>:1:1: error:
• Could not deduce (Runnable (ReaderT r0 Maybe a0) (() -> t))
(maybe you haven't applied a function to enough arguments?)
from the context: (Runnable (ReaderT r Maybe a) (() -> t), Num a)
bound by the inferred type for ‘it’:
forall r a t. (Runnable (ReaderT r Maybe a) (() -> t), Num a) => t
at <interactive>:1:1-19
The type variables ‘r0’, ‘a0’ are ambiguous
• In the ambiguity check for the inferred type for ‘it’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the inferred type
it :: forall r a t. (Runnable (ReaderT r Maybe a) (() -> t), Num a) => t
run
的输出类型完全由其输入类型决定。将此表示为功能依赖项(-XFunctionalDependencies
):
class Runnable a b | a -> b where
run :: a -> b
-- side note: (//) does not belong here
(//) :: Runnable a b => a -> b
(//) = run
-- instances as given
现在可以了。您的版本不起作用的原因是因为无法知道输出类型应该是什么。如果您有,例如
act :: ReaderT Env Identity Ret
然后
run act :: Runnable (ReaderT Env Identity Ret) b => b
然后我们陷入困境,无法确定b
应该是什么。例如可以添加另一个实例
instance Runnable (ReaderT r m a) (ReaderT r m a) where
run = id
现在run act
可以是ReaderT Env Identity Ret
或Env -> Identity Ret
。依赖项a -> b
a)允许我们从b
推断a
,而b)限制instance
声明使之成为可能。像我给的实例那样有冲突的实例被拒绝,并且根据需要,run act
具有通过查看您给的b
推断为Env -> Identity Ret
的instance
类型。
简短回答: