类型签名过于笼统; 缺少约束?

所以我试着问一个将军:“你怎么调试类型编程问题?” 问题,似乎或者这个问题太笼统,无法回答,或者也许不可能调试这样的事情。 : - }

在这个特定的日子,令我感到厌烦的是一个中等大小,复杂的程序。 我已经设法将它归结为一个仍然出错的合理小核心。 开始了:

{-#
  LANGUAGE
    GADTs, MultiParamTypeClasses,
    FlexibleInstances, FlexibleContexts, RankNTypes,
    KindSignatures
#-}

module Minimal where

-------------------------------------------------------------------------------

data Union (t :: * -> *) (ts :: * -> *) x

class Member (x :: * -> *) (ys :: * -> *) where
instance Member x (Union x ys) where
instance (Member x ys) => Member x (Union y ys) where

-------------------------------------------------------------------------------

data ActM (effects :: * -> *) x
instance Monad (ActM effects) where

-------------------------------------------------------------------------------

data Reader i x where
  Get :: Reader i i

get :: Member (Reader i) req => ActM req i
get = undefined

runReader :: i -> ActM (Union (Reader i) req) x -> ActM req x
runReader = undefined

(另外:我查看了这个语言扩展名列表,并认为“也许这只是一个可怕的主意!”)

无论如何,真正有趣的部分是在底部。 我们看到了

runReader :: i -> ActM (Union (Reader i) req) x -> ActM req x

换句话说,对于任何irunReaderActM monad中执行一个操作,将Reader i作为其可能影响之一,并在ActM monad中返回一个操作,但不会产生可能的影响。 很简单,对吧?

现在,如果我传递(比如说)一个Char作为第一个参数,那么阅读器类型被固定为Char

runReader 'X' :: ActM (Union (Reader Char) req) x -> ActM req x

到现在为止还挺好。 如果我只是返回一些数据,一切都很好:

runReader 'X' (return True) :: ActM req Bool

(令人欣慰的是,我也得到了正确的价值!)

但是现在, get函数呢?

get :: Member (Reader i) req => ActM req i

所以getActM monad中针对包含Reader i任何一组效果的操作。 这意味着如果我将它传递给runReader那么我就会得到...

runReader 'X' get :: Member (Reader x) (Union (Reader Char) req) => ActM req x

...等等,什么?!? 为什么编译器不知道x必须是Char

事实上,如果我添加一个明确的类型签名说

runReader 'X' get :: ActM req Char

那么它编译完美(顺便说一下,我得到正确的值输出,这很好)。 那么我在哪里错过了一个约束?


编译器没有发现x必须是Char因为它不是真的。 例如,有人可能会写

type ReadsBool req = Union (Reader Bool) req
type ReadsCharAndBool req = Union (Reader Char) (ReadsBool req)

然后你有:

runReader 'X' (get :: ReadsCharAndBool req) :: ActM (ReadsBool req) Bool

所以,恭喜,现在你知道为什么mtl的MonadReader类具有它的fundep:为了类型推断的原因,人们通常希望monad能够唯一确定你可以get什么样的东西。

链接地址: http://www.djcxy.com/p/43037.html

上一篇: Type signature is too general; missing constraint?

下一篇: implicitly coerce types in a Haskell do block?