2015-12-21 20 views
7

記住,這反射程序:使用類型註釋編譯兩個函數。刪除一個註釋 - 不編譯。再次刪除兩個 - 編譯。爲什麼?

{-# 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.

爲什麼出現這種情況?

+0

刪除類型時推斷的類型是什麼?他們如何與提供的類型進行比較?這是一種推測,但我認爲推斷或提供的類型之一不夠通用,因此呈現不兼容。另外添加推斷的類型可以幫助回答這個問題。 – concept3d

+2

我已經在這個問題上包含了推斷類型,但我無法理解它們,因爲有蜘蛛。 – MaiaVictor

+2

單態限制 –

回答

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運行,並且它會在每個配置中成功編譯。 Hereareafewtests

+0

謝謝。在這一點上,我認爲如果我應該禁用默認的單態限制。 – MaiaVictor

1

正如裏德巴頓在評論中指出的,這是由於The Dreaded Monomorphism Restriction

這裏被簡化例如:

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

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

當啓用單態的限制和foo的類型簽名說:

  • GHC試圖單態類型分配給到foo和失敗,因爲沒有缺省Monad實例:

否實例(單子M0)從使用的「>> =」產生
的類型變量「M0」是

  • 使用foobar導致我不能解釋
  • 另一個錯誤曖昧

無法與 'M'
因爲類型變量 'M' 將逃脫其範圍

匹配類型 'M0' 0

添加{-# LANGUAGE NoMonomorphismRestriction #-}編譯指示修復此問題並允許增量添加類型簽名。

+0

'增加{ - #LANGUAGE NoMonomorphismRestriction# - }編譯指示修復了這個問題並且允許增量地添加類型簽名。「 - 這總是正確的嗎? – MaiaVictor

+0

是的,如果錯誤是由單態限制引起的。缺點是,默認情況下禁用MR可導致共享減少,但這種情況非常罕見。 –

+0

[這裏](http://lambda.jstolarek.com/2012/05/towards-understanding-haskells-monomorphism-restriction/)解釋了MR如何影響分享。 –