缺少类型推断导致编译失败,没有实例模糊

gat*_*ado 5 haskell type-inference typeclass ghc

我很困惑为什么这个代码用类型提示编译,但没有编译.不应该有任何实例歧义(有一个实例).

class Monad m => FcnDef ? m | ? -> m where
    def :: String -> ? -- takes a name

instance Monad m => FcnDef (m ? -> m ?) m where
    def s body = body

dummyTest :: forall m. Monad m => m ()
dummyTest = def "dummy" ((return ()) :: m ())
Run Code Online (Sandbox Code Playgroud)

另一方面,如果省略:: m ()或所有类型声明,编译将失败并显示此错误,

No instance for (FcnDef (m0 () -> t0) m0)
  arising from a use of `def'
Run Code Online (Sandbox Code Playgroud)

为了澄清,代码试图为多变量类型def,因此可以编写例如

def "dummy2" "input" $ \in -> return ()
Run Code Online (Sandbox Code Playgroud)

编辑

这个问题比没有单态限制问题更有趣.如果添加了这样的代码,则将实例解析为具体类型,即

dummyTest = def "dummy" (return ())
g :: IO ()
g = dummyTest
Run Code Online (Sandbox Code Playgroud)

编译同样失败.

ham*_*mar 7

外部类型签名的需要是由单态限制引起的.

这个的赠品是你定义的左侧.

dummyTest = ...
Run Code Online (Sandbox Code Playgroud)

由于此定义没有任何参数,编译器将尝试使定义为单态.添加类型签名会覆盖此行为.

但是,正如您所指出的,这还不够.由于某种原因,编译器无法推断内部表达式的类型.为什么?我们来看看.是时候玩类型推理引擎了!

让我们从外部类型开始,尝试计算出内部表达式的类型.

dummyTest :: forall m. Monad m => m ()
dummyTest = def "dummy" (return ())
Run Code Online (Sandbox Code Playgroud)

类型defFcnDef ? m => String -> ?,但这里我们已经应用def了两个参数.这告诉我们?必须是一个函数类型.我们称之为x -> y.

然后我们可以很容易地推断出y必须等于m (),以满足外部类型.此外,参数的类型return ()Monad m1 => m1 (),因此我们可以推断出def我们正在寻找的类型必须具有类型FcnDef (m1 () -> m ()) m0 => def :: String -> m1 () -> m ().

接下来,我们将继续查找要使用的实例.唯一可用的实例不够通用,因为它需要m1并且m是相同的.所以我们大声抱怨这样的消息:

Could not deduce (FcnDef (m1 () -> m ()) m0)
  arising from a use of `def'
from the context (Monad m)
  bound by the type signature for dummyTest :: Monad m => m ()
  at FcnDef.hs:10:1-51
Possible fix:
  add (FcnDef (m1 () -> m ()) m0) to the context of
    the type signature for dummyTest :: Monad m => m ()
  or add an instance declaration for (FcnDef (m1 () -> m ()) m0)
In the expression: def "dummy" ((return ()))
In an equation for `dummyTest':
    dummyTest = def "dummy" ((return ()))
Run Code Online (Sandbox Code Playgroud)

这里要注意的关键是,当我们试图推断出类型时,一个特定实例碰巧躺在身边的事实并没有影响我们的选择.

因此,我们不得不使用范围类型变量手动指定此约束,或者我们可以在类型类中对其进行编码.

class Monad m => FcnDef ? m | ? -> m where
    def :: String -> ? -> ?

instance Monad m => FcnDef (m ?) m where
    def s body = body

-- dummyTest :: forall m. Monad m => m ()
dummyTest = def "dummy" (return ())
Run Code Online (Sandbox Code Playgroud)

现在,类型推理引擎可以很容易地确定monad return必须与结果中的monad相同,并且使用NoMonomorphismRestriction我们也可以删除外部类型签名.

当然,我并不是百分之百确定你在这里想要完成什么,所以你必须在你想要做的事情中判断这是否有意义.