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

标签 haskell monads typeclass monad-transformers

我如何编写一个通用函数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/

相关文章:

haskell - 使用 foldr 的功能太急切了

haskell - 从约束对产品成立的事实证明约束对产品的某个组成部分成立

haskell - 错误类型类的使用

haskell - 类型类 : function with default implementation vs separate function

haskell - 为什么Prelude的单词功能不写得简单一些呢?

haskell - 使用模板 Haskell 时键入同义词 "not in scope"

scala - 使类型签名独立于特定的 monad 转换器堆栈 (Scala)

Haskell:重复函数(+)和(++),mappend

haskell - 在模式保护或 case 表达式中重用模式

scala - 如果数据结构是可折叠的,它是幺半群吗?