haskell - 缺乏类型推断导致编译失败,没有实例歧义

标签 haskell type-inference ghc typeclass

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

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 ())

另一方面,如果省略 :: m () ,或所有类型声明,编译失败并出现此错误,
No instance for (FcnDef (m0 () -> t0) m0)
  arising from a use of `def'

为了澄清起见,该代码试图为 def 创建一个多变量类型。 ,所以可以写例如
def "dummy2" "input" $ \in -> return ()

编辑

这个问题比无单态限制问题更有趣。如果添加这样的代码,则将实例解析为具体类型,即
dummyTest = def "dummy" (return ())
g :: IO ()
g = dummyTest

编译同样失败。

最佳答案

需要外部类型签名是由 monomorphism restriction 引起的.

对此的赠品是您定义的左侧。

dummyTest = ...

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

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

让我们从外部类型开始,并尝试计算出内部表达式的类型。
dummyTest :: forall m. Monad m => m ()
dummyTest = def "dummy" (return ())
def的类型是 FcnDef β m => String -> β ,但在这里我们申请了 def到两个论点。这告诉我们β必须是函数类型。我们叫它x -> y .

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

接下来,我们将继续查找要使用的实例。唯一可用的实例不够通用,因为它需要 m1m是相同的。所以我们用这样的消息大声提示:
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 ()))

这里要注意的关键是,当我们试图推断类型时,一个特定实例碰巧在附近的事实不会影响我们的选择。

因此,我们不得不使用作用域类型变量手动指定此约束,或者我们可以在类型类中对其进行编码。
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 ())

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

当然,我不能 100% 确定您要在这里完成什么,因此您必须判断这在您要做什么的背景下是否有意义。

关于haskell - 缺乏类型推断导致编译失败,没有实例歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7224120/

相关文章:

Haskell 和随机数

rust - Rust 的类型推断如何跨多个语句工作?

c - pthread_cond_timedwait 不会在 GHC FFI 中返回

haskell /亚森 : output JSON as one object

haskell 二叉树签名术语代数计算大小

haskell - Haskell 中用于比较列表的 Eq 问题

haskell - MaybeT 的这种特殊用途是如何运作的?

c# - 使用解构元组赋值扩展方法进行类型推断

java - 使用本地类型推断的程序可以在较低的 Java 版本上运行吗?

haskell - 如何在用户事件中断计算的情况下逐步更新 Haskell 中的 GUI?