组合状态和IO效果时出现奇怪的`更自由`类型错误

时间:2017-02-13 18:34:16

标签: haskell

我试图运行这个更自由的代码:

{-# LANGUAGE DataKinds        #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs            #-}
{-# LANGUAGE TupleSections    #-}
{-# LANGUAGE TypeOperators    #-}

module Lib where

import           Control.Monad.Freer
import           Control.Monad.Freer.Internal
import           Control.Monad.Freer.State
import           Data.IORef

runStateIO :: (Member (State s) r, Member IO r) => IORef s -> Eff (State s ': r) a -> Eff r (a,s)
runStateIO ref = loop
  where
    loop (Val v) = (v,) <$> send (readIORef ref)
    loop (E u q) =
      case decomp u of
        Right Get     -> send (readIORef ref) >>= loop . qApp q
        Right (Put s) -> send (writeIORef ref s) >> loop (qApp q ())
        Left u'       -> E u' (tsingleton (loop . qApp q))

statefulFac :: Member (State Int) r => Int -> Eff r ()
statefulFac 1 = return ()
statefulFac n = do
    a <- get
    put (n * a)
    statefulFac (n - 1)

runStatefulFac_IO :: Int -> IO Int
runStatefulFac_IO n = do
    ref <- newIORef 1 :: IO (IORef Int)

    let
      -- none of these work:
      -- stateful_fac :: (Member (State Int) r, Member IO r) => Eff r ()
      -- stateful_fac :: Eff '[IO, State Int] ()
      -- stateful_fac :: Eff '[State Int, IO] ()
      stateful_fac = statefulFac 5

      r = runStateIO ref stateful_fac

    -- runM r
    undefined

runStatefulFac_pure :: Int -> Int
runStatefulFac_pure n = snd (run (runState (statefulFac n) 1))

这是使用股票freer,除了我导出State效果的构造函数以便能够运行runStateIO解释器。问题是它失败了

example.hs:42:11: error:
    • Ambiguous type variable ‘r0’ arising from a use of ‘runStateIO’
      prevents the constraint ‘(Data.Open.Union.Internal.Member'
                                  (State Int)
                                  r0
                                  (Data.Open.Union.Internal.FindElem
                                     (State Int) r0))’ from being solved.
      Relevant bindings include
        r :: Eff r0 ((), Int) (bound at example.hs:42:7)
        stateful_fac :: Eff (State Int : r0) () (bound at example.hs:40:7)
      Probable fix: use a type annotation to specify what ‘r0’ should be.
      These potential instances exist:
        two instances involving out-of-scope types
          instance [safe] (r ~ (t' : r' : rs'),
                           Data.Open.Union.Internal.Member' t (r' : rs') n) =>
                          Data.Open.Union.Internal.Member'
                            t r ('Data.Open.Union.Internal.S n)
            -- Defined in ‘Data.Open.Union.Internal’
          instance [safe] r ~ (t : r') =>
                          Data.Open.Union.Internal.Member' t r 'Data.Open.Union.Internal.Z
            -- Defined in ‘Data.Open.Union.Internal’
    • In the expression: runStateIO ref stateful_fac
      In an equation for ‘r’: r = runStateIO ref stateful_fac
      In the expression:
        do { ref <- newIORef 1 :: IO (IORef Int);
             let stateful_fac = statefulFac 5
                 r = runStateIO ref stateful_fac;
             undefined }

我想知道(1)此消息试图说明(2)此代码有什么问题以及如何解决?感谢。

1 个答案:

答案 0 :(得分:3)

我认为这是签名

runStateIO :: (Member (State s) r, Member IO r) => IORef s -> Eff (State s ': r) a -> Eff r (a,s)

您不应该在约束中Member (State s) r,因为您直接谈论State s ': r。所以,如果你写

runStateIO :: ( Member IO r) => IORef s -> Eff (State s ': r) a -> Eff r (a,s)

然后main中您想要的签名应该可以正常工作

let stateful_fac = statefulFac 5 :: Eff '[State Int, IO] ()

State Int被解释后,编译器仍在寻找State Int成为r成员的方法。因此,如果我使用上面的签名stateful_fac编译器抱怨:

• Couldn't match type ‘'[]’ with ‘r'0 : rs'0’
    arising from a use of ‘runStateIO’

正确地说,因为它知道那里必须有另一个元素。