2012-03-14 46 views
4

我有這樣的代碼:轉換型家庭實例來詮釋

type family Id obj :: * 
type instance Id Box = Int 

我想讓它,所以我總是可以從ID類型的家庭得到詮釋。我承認需要進行轉換。

我想也許創建一個類將工作:

class IdToInt a where 
    idToInt :: Id a -> Int 

instance IdToInt Box where 
    idToInt s = s 

這實際上編譯。但是,當我嘗試使用它:

testFunc :: Id a -> Int 
testFunc x = idToInt x 

我得到錯誤:

src/Snowfall/Spatial.hs:29:22: 
Couldn't match type `Id a0' with `Id a' 
NB: `Id' is a type function, and may not be injective 
In the first argument of `idToInt', namely `x' 
In the expression: idToInt x 
In an equation for `testFunc': testFunc x = idToInt x 

所以,我怎樣才能創建一個類型系列ID來獲得一個int轉換?

基於由ehird答案,我嘗試以下,但它不工作之一:

class IdStuff a where 
    type Id a :: * 
    idToInt :: Id a -> Int 

instance IdStuff Box where 
    type Id Box = Int 
    idToInt s = s 

testFunc :: (IdStuff a) => Id a -> Int 
testFunc x = idToInt x 

它提供了錯誤:

src/Snowfall/Spatial.hs:45:22: 
Could not deduce (Id a0 ~ Id a) 
from the context (IdStuff a) 
    bound by the type signature for 
      testFunc :: IdStuff a => Id a -> Int 
    at src/Snowfall/Spatial.hs:45:1-22 
NB: `Id' is a type function, and may not be injective 
In the first argument of `idToInt', namely `x' 
In the expression: idToInt x 
In an equation for `testFunc': testFunc x = idToInt x 

回答

2

正如其他人指出的,問題是,編譯器無法弄清楚哪個a使用。數據族是一種解決方案,但有時更容易使用的替代方法是使用類型見證。

更改類

class IdToInt a where 
    idToInt :: a -> Id a -> Int 

instance IdToInt Box where 
    idToInt _ s = s 

-- if you use this a lot, it's sometimes useful to create type witnesses to use 
box = undefined :: Box 

-- you can use it like 
idToInt box someId 

-- or 
idToInt someBox (getId someBox) 

你需要回答的問題是,對於任何給定Id,是隻有一種類型a它應該出現?也就是說,a s和Id a s之間是否存在一對一的對應關係?如果是這樣,數據族是正確的方法。如果沒有,你可能更喜歡見證人。

3

你不能。你需要testFunc :: (IdToInt a) => Id a -> Int。類型家族是開放的,所以任何人都可以隨時聲明

type instance Id Blah =() 

並且不提供轉換功能。最好的辦法是把類型家庭放在課堂上:

class HasId a where 
    type Id a 
    idToInt :: Id a -> Int 

instance IdToInt Box where 
    type Id Box = Int 
    idToInt s = s 

不過你仍然需要上下文。

+0

謝謝。我仍然沒有得到它。我根據你在問題中的回答發佈了我的結果,以便它能夠正確格式化。 – mentics 2012-03-14 16:55:37

+2

@taotree:哦,那是因爲你正在使用類型同義詞族,而不是數據類型族。這個具體的問題實際上可能是一個bug,但總的來說,類型同義詞族很沒用;因爲兩個實例完全可能具有相同的關聯類型,所以GHC幾乎放棄了推演任何事情,並最終導致混亂。使用數據類型系列將解決所有這些問題。 – ehird 2012-03-14 16:59:44

3

您不能使用IdToInt a => Id a -> Int類型的函數,因爲無法確定a是什麼類型。以下示例演示了這一點。

type family Id a :: * 
type instance Id() = Int 
type instance Id Char = Int 

class IdToInt a where idToInt :: Id a -> Int 

instance IdToInt() where idToInt x = x + 1 
instance IdToInt Char where idToInt x = x - 1 

main = print $ idToInt 1 

因爲Id() = Id Char = Int,的idToInt在上述背景下的類型是Int -> Int,其等於Id() -> IntId Char -> Int。請記住,根據類型選擇重載方法。這兩個類實例都定義了idToInt函數,其類型爲Int -> Int,因此類型檢查器無法決定使用哪一個函數。

您應該使用數據族而不是類型族,並聲明newtype實例。

data family Id a :: * 
newtype instance Id() = IdUnit Int 
newtype instance Id Char = IdChar Int 

隨着NEWTYPE實例,Id()Id Char都是整數,但他們有不同的類型。 Id的類型通知類型檢查器哪個超載函數要使用。