我如何编写一个函数 `运行` 类 `运行State` 或 `运行ReaderT`?
How can I write a function `run` that calls `runStateT` or `runReaderT`?
我如何编写一个通用函数 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
,它只是 returns 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) 允许我们从 a
推断出 b
和 b) 限制 instance
声明使之成为可能。像我给出的那样的冲突实例被拒绝,并且 run act
通过查看你给出的 instance
将 b
类型推断为 Env -> Identity Ret
,如你所愿。
简短回答: 您需要 class.
的功能依赖
长答案:
当编译器看到 run
时,它需要找到 Runnable
的适当实例,以确定要使用 run
的哪个实现。为了找到那个实例,它需要知道 a
和 b
是什么。它知道a
是一个ReaderT
,所以一个就被覆盖了。但是 b
是什么?
编译器发现您正在使用 b
作为函数,并将 ()
作为参数传递给它。因此,编译器认为 b
必须是 () -> t
的形状,其中 t
还未知。
这就是它有点停止的地方:编译器无处可以从中获取 t
,因此它不知道 b
,因此找不到合适的实例,所以 kaboom !
但是有一种方法可以解决这种情况。如果我们仔细看看你的 Runnable
class 的实际含义,很容易看出 b
应该严格由 a
定义。也就是说,如果我们知道 monad 是什么,我们就知道 return 值是什么。因此,编译器应该可以通过知道a
来确定b
。但是,唉,编译器不知道!
但是有一种方法可以向编译器解释这一点。叫做"functional dependency",写法是这样的:
class Runnable a b | a -> b where
这个符号 a -> b
告诉编译器 b
应该由 a
明确确定。这将意味着,一方面,编译器不会让您定义违反此规则的实例,另一方面,它将能够通过知道 [=14] 来找到 Runnable
的适当实例=],然后根据该实例确定 b
。
我如何编写一个通用函数 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
,它只是 returns 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) 允许我们从 a
推断出 b
和 b) 限制 instance
声明使之成为可能。像我给出的那样的冲突实例被拒绝,并且 run act
通过查看你给出的 instance
将 b
类型推断为 Env -> Identity Ret
,如你所愿。
简短回答: 您需要 class.
的功能依赖长答案:
当编译器看到 run
时,它需要找到 Runnable
的适当实例,以确定要使用 run
的哪个实现。为了找到那个实例,它需要知道 a
和 b
是什么。它知道a
是一个ReaderT
,所以一个就被覆盖了。但是 b
是什么?
编译器发现您正在使用 b
作为函数,并将 ()
作为参数传递给它。因此,编译器认为 b
必须是 () -> t
的形状,其中 t
还未知。
这就是它有点停止的地方:编译器无处可以从中获取 t
,因此它不知道 b
,因此找不到合适的实例,所以 kaboom !
但是有一种方法可以解决这种情况。如果我们仔细看看你的 Runnable
class 的实际含义,很容易看出 b
应该严格由 a
定义。也就是说,如果我们知道 monad 是什么,我们就知道 return 值是什么。因此,编译器应该可以通过知道a
来确定b
。但是,唉,编译器不知道!
但是有一种方法可以向编译器解释这一点。叫做"functional dependency",写法是这样的:
class Runnable a b | a -> b where
这个符号 a -> b
告诉编译器 b
应该由 a
明确确定。这将意味着,一方面,编译器不会让您定义违反此规则的实例,另一方面,它将能够通过知道 [=14] 来找到 Runnable
的适当实例=],然后根据该实例确定 b
。