我如何编写调用runStateT或runReaderT的函数run?

问题描述 投票:0回答:2

我如何编写一个通用函数run,该函数采用某个monad转换器的对象并调用相应的函数?

给出run s

  • 如果sStateT,则run = runStateT
  • 如果sReaderT,则run = runReaderT
  • 如果sMaybeT,则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
haskell monads typeclass monad-transformers
2个回答
1
投票

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 RetEnv -> Identity Ret。依赖项a -> b a)允许我们从b推断a,而b)限制instance声明使之成为可能。像我给的实例那样有冲突的实例被拒绝,并且根据需要,run act具有通过查看您给的b推断为Env -> Identity Retinstance类型。


0
投票

简短回答:

© www.soinside.com 2019 - 2024. All rights reserved.