2013-09-22 18 views
3

我試圖讓下面的代碼工作(當然,編譯第一!):如何從類型函數中「推導出」其中的一個實例時使用多個「數據」實例?

module Orexio.Radix where 

import Data.Data 
import Data.Map (Map) 
import qualified Data.Map as Map 
import Data.Typeable 
import Text.JSON.Generic 

class Resource a where 
    type Representation a :: * 
    identifier :: Resource a => Identifier a 

class Endpoint a where 
    call :: Resource a => a -> Representation a 

data Identifier a = Identifier [String] deriving (Show) 

data Binding a = Binding (JSValue -> Either String JSValue) 

bind :: (Data a, Resource a, Endpoint a, Data (Representation a)) => Binding a 
bind = Binding (\x -> binding $ query x) 
    where binding query = fmap (\x -> toJSON $ call x) (resultToEither query) 
     query jsvalue = fromJSON jsvalue 

{-- DEMO --} 

data HelloWorld = HelloWorld { 
    name :: String 
} deriving (Show, Typeable, Data) 

instance Resource HelloWorld where 
    type Representation HelloWorld = String 
    identifier = Identifier ["helloworld"] 

instance Endpoint HelloWorld where 
    call r = "Hello " ++ name r 

所以我不得不啓用FlexibleContexts能夠做到Data (Representation a),但它仍然不工作...

我有這樣的錯誤:

src/Orexio/Radix.hs:21:33: 
    Could not deduce (Data a0) arising from a use of `query' 
    from the context (Data a, 
         Resource a, 
         Endpoint a, 
         Data (Representation a)) 
     bound by the type signature for 
       bind :: (Data a, Resource a, Endpoint a, 
          Data (Representation a)) => 
         Binding a 
     at src/Orexio/Radix.hs:20:9-78 
    The type variable `a0' is ambiguous 
    Possible fix: add a type signature that fixes these type variable(s) 
    Note: there are several potential instances: 
     instance Data HelloWorld -- Defined at src/Orexio/Radix.hs:29:29 
     instance Data() -- Defined in `Data.Data' 
     instance (Data a, Data b) => Data (a, b) -- Defined in `Data.Data' 
     ...plus 42 others 
    In the second argument of `($)', namely `query x' 
    In the expression: binding $ query x 
    In the first argument of `Binding', namely 
     `(\ x -> binding $ query x)' 

老實說我還挺丟在這裏,我必須失去了一些東西,但什麼?

這裏是我已經激活了其他擴展:DeriveDataTypeableExistentialQuantificationNoMonomorphismRestrictionTypeFamilies

提前感謝!

+0

按照建議GHC給你:「可能的修復:添加修復這些類型變量的類型簽名」 –

+0

@SjoerdVisscher我試圖把它們放在不同的位置(在查詢RHS和綁定RHS),但沒有成功。你會把他們放在哪裏? –

回答

8

看起來像Binding a應該是一個函數,它將類型爲a的值轉換爲類型爲Representation a的值或錯誤消息。由於輸入和輸出是JSON編碼的,但它們都有JSValue;他們的類型根本沒有提及a

data Binding a = Binding (JSValue -> Either String JSValue) 

沒有關於這些JSValue代表什麼類型的信息。

bind的定義中,編譯器知道返回類型爲Binding a,但該類型與JSValues類型之間沒有鏈接。特別是,編譯器不能推斷fromJSON應該返回atoJSON應該帶Representation a。要修復它,請將顯式類型簽名添加到bindingquery

有時讓人困惑的一個細節是如何告訴GHC關於類型變量作用域。這需要ScopedTypeVariables擴展名。將forall a.添加到bind的類型簽名中,並將forall.添加到bind正文中的其他類型簽名,以便變量a的範圍適當。

+0

感謝您的回答我現在更好地理解了這個問題,但添加了類型註釋後我還在掙扎,下面是我現在擁有的:http:// lpaste。net/93263,但它仍然沒有編譯:-( –

+0

@Alois,從綁定和查詢中刪除這些函數和約束。使用函數創建新的類型變量,但是關鍵是要使用bind中的'a' '簽名。 –

+0

@AloisCochard,你還打開了ScopedTypeVariables? –

3

類型錯誤的實質是這一行:「類型變量`a0'不明確」。

(聲明:我試圖避免在這個答案行話)

要明白是怎麼回事,我建議浮動bindingquery綁定頂級。如果您然後註釋掉bind綁定,他們會成功地進行類型檢查。 GHC推斷出以下類型。

*Orexio.Radix> :i query 
query :: Data a => JSValue -> Result a 
*Orexio.Radix> :i binding 
binding :: 
    (Data (Representation a), Endpoint a, Resource a) => 
    Result a -> Either String JSValue 

你的錯誤基本上是由在bind定義中的表達\x -> binding (query x)引起的。請注意,類型變量a僅出現在binding的域和query的範圍內。因此,當你編寫它們時,會發生兩件事情。

  1. 類型變量不是確定;其「價值」仍然未知。

  2. 類型變量無法從 組合的類型中獲得。對於我們的非正式用途,該類型爲JSValue -> Either String JSValue

因爲該組合物過程中沒有確定的類型變量GHC引發錯誤(即1)它不能在未來(2的結果)確定。

Haskell中此問題的一般形狀更常稱爲「show-read問題」;在Real World Haskell的Chapter 6上搜索「模棱兩可」。 (有些人也可以稱之爲「太多多態性」。)

當你和Sjoerd已經確定(和RWH章解釋),您可以通過應用binding之前歸咎於一個類型的query結果解決這種類型的錯誤。如果不知道你的語義,我假設你打算將這個「隱藏」類型變量aBinding類型構造函數的參數相同。所以下面的工作。

bind :: forall b. 
(Data b, Resource b, Endpoint b, Data (Representation b)) => Binding b 
bind = Binding (\x -> binding $ (query x :: Result b)) 

此歸屬消除可變a通過完全用Result b替換它的類型。請注意,與a不同,b可以保持未定,因爲它可以在頂層類型中訪問; bind的使用可以各自確定b

第一個解決方案需要給bind一個明確的簽名 - 這有時會非常繁瑣。在這種情況下,由於單態限制,您可能已經需要該類型簽名。但是,如果bind接受了參數(但仍顯示此歧義類型變量錯誤),則仍然可以使用類似於以下解決方案的類型推斷。

dummy_ResultType :: Binding a -> Result a 
dummy_ResultType = error "dummy_ResultType" 

bind() = result where 
    result = Binding (\x -> binding $ (query x `asTypeOf` dummy)) 
    dummy = dummy_ResultType result 

(如果使用的error您的顧慮,比照Proxy type

或行業的一些習語爲直接:

withArgTypeOf :: f x -> g x -> f x 
withArgTypeOf x _ = x 

bind() = result where 
    result = Binding (\x -> binding (query x `withArgTypeOf` result)) 

現在推理作品。

*Orexio.Radix> :i bind 
bind :: 
    (Data (Representation a), Data a, Endpoint a, Resource a) => 
() -> Binding a 

請放心,GHC會在類型檢查後迅速確定該定義實際上不是遞歸的。

HTH。

相關問題