2015-08-20 40 views
1

我正嘗試創建自定義數據類型。作爲一個例子使用約束創建自定義數據類型

data Time = Second Int deriving (Show) 

但是,這是太有限(我們可以說以後需要毫秒)。我想,而不是定義是這樣的:

data Time = Second Num deriving (Show) 

這並不編譯,因爲Num有種* -> ghc-prim-0.4.0.0:GHC.Prim.Constraint

如何設置Time這樣Second可以包含任何Num

+0

你確定有一個任意的'Num'?如果我在'Import Data.Complex'之後做了'秒(1:+ 2)'會怎麼樣?允許複雜的時間有意義嗎?相反,最好是有多個構造函數,所以'data Time = Seconds Int |毫秒Int |微秒Int |分鐘Int |小時國際? – bheklilr

+1

根據F先生和本,似乎標準沒有任何限制(更不用說Num了)。 – GeneralBecos

回答

2

有一個名爲數據類型的上下文棄用的功能,可以讓你做到這一點:

{-# LANGUAGE DatatypeContexts #-} 
data Num a => Time a = Second a deriving (Show) 
t = Second (5 :: Int) 
main = print t 

這對執行GHC 7.8.3(對不起,沒有7.10檢查),但警告你這個折舊:

t.hs:1:14: Warning: 
    -XDatatypeContexts is deprecated: It was widely considered a 
misfeature, and has been removed from the Haskell language. 
Second 5 

非過時的方式來做到這一點是使用Generalized Algebraic Datatypes(GADTs)(並且你需要獨立deriving以及):

{-# LANGUAGE GADTs, StandaloneDeriving #-} 
data Time a where 
    Second :: Num a => a -> Time a 
deriving instance Show a => Show (Time a) 
t = Second (5 :: Int) 
main = print t 

如果試圖創建一些變量非Num,你會得到一個編譯錯誤:

t = Second "a" 

t.hs:12:5: 
    No instance for (Num [Char]) arising from a use of ‘Second’ 
    In the expression: Second "a" 
    In an equation for ‘t’: t = Second "a" 
+0

謝謝! : - )...現在瞭解GADT ;-) – GeneralBecos

+0

@GeneralBecos,GADT很棒,但我不認爲這是它們的一個很好的應用。 – dfeuer

+0

同意。 F先生在爲什麼給出了一個很好的理由。乾杯! – GeneralBecos

3

之一的,爲什麼這可能不是那麼理想的最好的例子是found here在維基教科書關於類和類型的部分。他們說:

Type constraints in data declarations are less useful than it might seem at first. Consider:

data (Num a) => Foo a = F1 a | F2 a String 

Here, Foo is a type with two constructors, both taking an argument of a type a which must be in Num. However, the (Num a) => constraint is only effective for the F1 and F2 constructors, and not for other functions involving Foo. Therefore, in the following example...

fooSquared :: (Num a) => Foo a -> Foo a 
fooSquared (F1 x) = F1 (x * x) 
fooSquared (F2 x s) = F2 (x * x) s 

... even though the constructors ensure a will be some type in Num we can't avoid duplicating the constraint in the signature of fooSquared

這表明,你一個合理的選擇是隻創建Time與通用參數,再後來確保上Time數據操作模塊的功能總是有必要的約束爲Num

這不會是這麼多一份擔心有人熄滅,愚蠢使得Time String什麼的 - 如果他們這樣做,那麼所有的提供的模塊功能都將是對他們有幫助的,所以也沒有非常重要。

還有幾個選項查找與GADTs的{-# LANGUAGE GeneralizedNewtypeDeriving #-}編譯和{-# LANGUAGE DatatypeContexts #-}編譯。但通常情況下,這些都會導致不必要的額外複雜度,特別是如果你是像我這樣的Haskell新手。

+1

您對數據類型無約束的建議絕對是執行此操作的標準方法。甚至有人甚至可能會在某天使用不同的數據類型限制版本(例如,使用僅僅可添加的時間來工作可能是有意義的)。如果你需要限制數據結構本身,那麼GADT是正確的方式,但是請注意,每個'Time'值都包含一個'Num'實例,因此函數知道'Num'的哪個實現函數來調用。 – Ben

+0

謝謝,我將不受限制地離開這個類型。 – GeneralBecos