我很困惑为什么这段代码使用类型提示编译,但没有编译就无法编译。不应该有任何实例歧义(有一个实例)。
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 ()
.接下来,我们将继续查找要使用的实例。唯一可用的实例不够通用,因为它需要
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 ()))
这里要注意的关键是,当我们试图推断类型时,一个特定实例碰巧在附近的事实不会影响我们的选择。
因此,我们不得不使用作用域类型变量手动指定此约束,或者我们可以在类型类中对其进行编码。
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/