3

我试图允许在我的 Free monad 中嵌入一个 state monad;这是我的简单尝试:

{-# language FlexibleInstances, MultiParamTypeClasses #-}
module Main where

import Control.Monad.Free
import Control.Monad.State
import Data.Bifunctor

data Toy state next =
      Output String next
    | LiftState (state -> (next, state))
    | Done

instance Functor (Toy s) where
  fmap f (Output str next) = Output str $ f next
  fmap f (LiftState stateF) = LiftState (first f . stateF)
  fmap f Done = Done

instance MonadState s (Free (Toy s)) where
  state = overState

overState :: (s -> (a, s)) -> Free (Toy s) a
overState = liftF . LiftState

output :: Show a => a -> Free (Toy s) ()
output x = liftF $ Output (show x) ()

done :: Free (Toy s) r
done = liftF Done

program :: Free (Toy Int) ()
program = do
  start <- get
  output start
  modify ((+10) :: (Int -> Int))
  end <- get
  output end
  done

interpret :: (Show r) => Free (Toy s) r -> s -> IO ()
interpret (Free (LiftState stateF)) s = let (next, newS) = stateF s
                                         in interpret next newS
interpret (Free (Output str next)) s = print str >> interpret next s
interpret (Free Done) s = return ()
interpret (Pure x) s = print x

main :: IO ()
main = interpret program (5 :: Int)

我得到错误:

• Overlapping instances for MonadState Int (Free (Toy Int))
    arising from a use of ‘get’
  Matching instances:
    instance [safe] (Functor m, MonadState s m) =>
                    MonadState s (Free m)
      -- Defined in ‘Control.Monad.Free’
    instance MonadState s (Free (Toy s))
      -- Defined at app/Main.hs:18:10
• In a stmt of a 'do' block: start <- get
  In the expression:
    do { start <- get;
         output start;
         modify ((+ 10) :: Int -> Int);
         end <- get;
         .... }
  In an equation for ‘program’:
      program
        = do { start <- get;
               output start;
               modify ((+ 10) :: Int -> Int);
               .... }

据我所知;它正在尝试应用此实例:

(Functor m, MonadState s m) => MonadState s (Free m)

这里的免费包中;但是在这种情况下,它必须匹配Free (Toy s)并且没有MonadState s (Toy s)要求,所以我不明白为什么它认为它适用。

如果我删除我的实例定义,我会得到:

• No instance for (MonadState Int (Toy Int))
    arising from a use of ‘modify’

这支持了我的想法,即另一个实例实际上并不适用;如何使用我指定的实例进行编译?你能解释为什么会这样吗?是因为FlexibleInstances被使用了吗?

谢谢!

4

1 回答 1

4

选择实例时会简单地忽略实例上下文((Functor m, MonadState s m)位)。这是为了防止编译器不得不进行可能成本高昂的回溯搜索来选择实例。因此,如果两个实例适用并且一个仅因为实例上下文而被排除,就像您的情况一样,它是重叠的。

这是 mtl 设计中不幸的部分,我认为每个 Haskell 程序员都曾在某些时候遇到过。修复的选择不多;通常您添加一个新类型并提供您的实例,如

newtype FreeToy s a = FreeToy (Free (Toy s) a)
instance MonadState s (FreeToy s) where -- ...
于 2017-01-19T21:26:50.813 回答