«MonadReader (Foo m) m» приводит к бесконечному типу из функциональной зависимости - PullRequest
0 голосов
/ 02 мая 2018

Я пытаюсь передать функцию в Reader, которая должна вызываться из той же монады, что и вызывающая функция, но я получаю ошибку бесконечного типа.

Упрощенный код:

{-# 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
   |                ^^

Любая помощь будет высоко ценится, спасибо!

1 Ответ

0 голосов
/ 02 мая 2018
runFoo :: MonadReader (Foo m) m => m ()

Давайте забудем о классе и предположим, что MonadReader env mon означает, что mon ~ ((->) env). Это соответствует простому использованию (->) в качестве нашей монады вместо любителя 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 ())
-- ==>
()
...