两个函数使用类型注释进行编译。删除一个注释 - 不编译。删除两个 - 再次编译。为什么?

时间:2015-12-21 14:45:27

标签: haskell types type-systems

记住这个反射计划:

{-# LANGUAGE ScopedTypeVariables, RecursiveDo #-}

import Control.Applicative
import Control.Monad
import Control.Monad.IO.Class
import Prelude hiding (div)
import Reflex.Dom
import qualified Data.Map as M

clickMe :: MonadWidget t m => m (Event t ())
clickMe = do
    rec (e,_) <- elAttr' "button" M.empty (display c)
        c :: Dynamic t Int <- count (domEvent Click e)
    return $ domEvent Click e

div :: forall t m a . MonadWidget t m => m a -> m a
div = elAttr "div" ("style" =: "border : 1px solid black")

app :: forall t m . MonadWidget t m => m ()
app = div $ do
    aClicks <- clickMe
    bClicks <- clickMe
    a <- count aClicks
    b <- count bClicks
    l <- combineDyn (\a b -> replicate (a-b) ()) a b
    simpleList l (const clickMe)
    return ()

main = mainWidget app

如果从divapp中删除类型注释,程序将无法使用巨大的可怕类型错误进行编译。 如果你删除它们,它将再次编译。从程序员的角度来看,当有人试图逐步注释未注释的程序时,这会给用户带来糟糕的体验。将未正确的类型注释添加到未注释的术语中会导致编译器错误,这会导致程序员认为他的类型错误。

This is the error you get by removing div's annotation.

Those are the inferred types.

为什么会这样?

2 个答案:

答案 0 :(得分:4)

这是由于单态性限制。当编译器在没有类型注释的情况下对顶级绑定进行类型检查时,如果该类型具有约束并且该函数没有语法参数,则不会分配多态类型,这是两者的情况。你的功能。

但是,如果您不包含 类型签名,则它仍然无法编译。在你的情况下,你给了它一些额外的信息(foo = [app, _]部分),并且由于某种原因它选择了单态类型 - 我不知道你的环境发生了什么变化,但这不是标准行为。

这是一个简单的文件,提炼出您遇到的问题:

{-# LANGUAGE RankNTypes, KindSignatures, MultiParamTypeClasses, FunctionalDependencies #-}

module Test where 

import Prelude hiding (div)

class MonadWidget t (m :: * -> *) | m -> t 

div :: forall t m a . MonadWidget t m => m a -> m a
div = (undefined :: forall t m a . MonadWidget t m => m a -> m a)

app :: forall t m . MonadWidget t m => m ()
app = (div (undefined :: forall t m . MonadWidget t m => m ())
        :: forall t m . MonadWidget t m => m () )

如果您注释掉任何类型签名,或两者都注释,您将遇到错误。 但是,注释掉任何顶级类型签名,但使用ghc -XNoMonomorphismRestriction Test.hs运行它,它将在每个配置中成功编译。 Here are a few tests

答案 1 :(得分:1)

正如Reid Barton在评论中指出的那样,这归因于The Dreaded Monomorphism Restriction

以下是简化示例:

foo :: Monad m => m a -> m a
foo = (>>= return)

bar :: Monad m => m ()
bar = foo (return ())

启用单态限制并且foo的类型签名被注释时:

  • GHC尝试将单态类型分配给foo并失败,因为没有默认的Monad实例:
  

因使用'&gt;&gt; ='而没有(Monad m0)的实例      类型变量'm0'是不明确的

  • foo使用bar会导致另一个我无法解释的错误
  

无法将类型'm0'与'm'匹配        因为类型变量'm'会逃避其范围

添加{-# LANGUAGE NoMonomorphismRestriction #-} pragma修复此问题并允许逐步添加类型签名。