haskell - "MonadReader (Foo m) m"函数依赖导致无限类型

标签 haskell types monads monad-transformers

我试图在 Reader 中传递一个函数,该函数将从与调用函数相同的 monad 中调用,但出现无限类型错误。

简化的代码是:

{-# LANGUAGE FlexibleContexts #-}

module G2 where

import Control.Monad
import Control.Monad.Reader

data Foo m = Foo { bar :: m () }

runFoo :: MonadReader (Foo m) m => m ()
runFoo = do
    b <- asks bar
    b

main :: Monad m => m ()
main = do
    let bar = return () :: m ()
        foo = Foo bar
    runReaderT runFoo foo

错误是:

    • Occurs check: cannot construct the infinite type:
        m0 ~ ReaderT (Foo m0) m
        arising from a functional dependency between:
          constraint ‘MonadReader
                        (Foo (ReaderT (Foo m0) m)) (ReaderT (Foo m0) m)’
            arising from a use of ‘runFoo’
          instance ‘MonadReader r (ReaderT r m1)’ at <no location info>
    • In the first argument of ‘runReaderT’, namely ‘runFoo’
      In a stmt of a 'do' block: runReaderT runFoo foo
      In the expression:
        do let bar = ...
               foo = Foo bar
           runReaderT runFoo foo
    • Relevant bindings include main :: m () (bound at G2.hs:16:1)
   |
19 |     runReaderT runFoo foo
   |                ^^

任何帮助将不胜感激,谢谢!

最佳答案

runFoo :: MonadReader (Foo m) m => m ()

让我们忘记这个类,并假设 MonadReader env mon 意味着 mon ~ ((->) env)。这相当于简单地使用 (->) 作为我们的 monad,而不是更高级的 ReaderT。然后你得到m ~ ((->) m) => m ()。您会发现 m 需要包含自身(具体来说,m 的参数是 m)。这对于值来说是可以的,但是如果类型检查器必须处理无限大的类型,那就很糟糕了。 ReaderT 也是如此(您需要使用 ReaderT,因为您调用了 runReaderT runFoo)。您需要定义另一个新类型来编码此递归:

data RecReader c a = RecReader { runRecReader :: c (RecReader c) -> a }
instance Functor (RecReader c) where
  fmap f (RecReader r) = RecReader $ f . r
instance Applicative (RecReader c) where
 pure = RecReader . const
 RecReader f <*> RecReader g = RecReader $ \e -> f e (g e)
instance Monad (RecReader c) where
  return = pure
  RecReader x >>= f = RecReader $ \e -> runRecReader (f (x e)) e
instance MonadReader (c (RecReader c)) (RecReader c) where
  ask = RecReader id
  local f (RecReader x) = RecReader $ x . f

它有效:

runRecReader runFoo (Foo $ return ())
-- ==>
()

关于haskell - "MonadReader (Foo m) m"函数依赖导致无限类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50139963/

相关文章:

haskell - 为什么 (Haskell) Repa 只使用一个 CPU?

c++ - C++ 中的引用是如何编码的?

types - Agda函数,类型上的函数匹配

haskell - monads 如何让我的工作更轻松?给我看一段很酷的代码

haskell - "all (==1) [1,1..]"没有终止的数学意义是什么?

haskell - 将 chr 的输出格式更改为十六进制

Haskell 新手理解函数组合

javascript - 字符串的 typeOf 正确使用

haskell - 在 Haskell 中,是否有 (liftM .liftM)、(liftM .liftM .liftM) 等的别名?

scala - free monad 和 AST 的关系