gpt4 book ai didi

haskell - 如何编写调用 `run` 或 `runStateT` 的函数 `runReaderT`?

转载 作者:行者123 更新时间:2023-12-02 05:56:14 25 4
gpt4 key购买 nike

我如何编写一个通用函数run,它接受一些 monad 转换器的对象,并调用相应的函数?

给定运行s

  • 如果 s 是一个 StateTrun = runStateT
  • 如果 s 是一个 ReaderTrun = runReaderT
  • 如果 s 是一个 MaybeTrun = 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 时,它需要找到 Runnable 的适当实例,以便确定要使用 run 的哪个实现。为了找到那个实例,它需要知道 ab 是什么。它知道 a 是一个 ReaderT,所以一个被覆盖了。但是 b 是什么?

编译器看到您正在使用 b 作为函数,将 () 作为参数传递给它。因此,编译器认为 b 的形状必须是 () -> t,其中 t 尚不清楚。

这就是它有点停止的地方:编译器没有地方可以从中获取 t,因此它不知道 b,因此它找不到合适的实例,如此轰隆!

但是对于这种情况是有补救办法的。如果我们仔细观察您的 Runnable 类的实际含义,很容易看出 b 应该由 a 严格定义。也就是说,如果我们知道 monad 是什么,我们就知道返回值是什么。因此,编译器应该可以通过知道a来确定b。但遗憾的是,编译器并不知道这一点!

但是有一种方法可以向编译器解释这一点。叫做“函数依赖”,是这样写的:

class Runnable a b | a -> b where

此符号 a -> b 告诉编译器 b 应该由 a 明确确定。这将意味着,一方面,编译器不会让您定义违反此规则的实例,另一方面,它将能够通过了解找到合适的 Runnable 实例a,然后根据该实例确定 b

关于haskell - 如何编写调用 `run` 或 `runStateT` 的函数 `runReaderT`?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58613931/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com