2016-02-23 78 views
1

我有一個參數化的類型,我想限制爲一個數字型,更具體地Fractional,例如:我可以在Haskell中的類型/數據構造函數上約束參數多態類型嗎?

data Rating a = (Fractional a) => Score a | Unscored deriving (Show, Eq) 

使得API的用戶可以定義他們可能利用該非整數型(FloatDouble?),但是我編寫的內部API代碼仍然可以對數字類型執行算術運算。我不希望它是一個整數,因爲我的「內部操作」的結果可能不是整數,我的理解是使用Fractional會導致更準確的結果。

編譯以上(以GHCI至少)給了我以下錯誤:

Data constructor `Score' has existential type variables, a context, or a specialised result type 
    Score :: forall a. Fractional a => a -> Rating a 
    (Use ExistentialQuantification or GADTs to allow this) 
In the definition of data constructor `Score' 
In the data declaration for `Rating' 

這表明我,我做的事情,我可能不想繼續努力;即我的設計是垃圾。

我想我在這個API中說:「當你使用Rating類型時,它的參數必須是Fractional的子類,所以我可以對它進行精確的算術運算」。我怎麼能做到這一點?或者我是否脫離了標記和/或過度工程?

+0

已經在[這個答案]中討論過(http://stackoverflow.com/questions/12770278/typeclass-constraints-on-data-declarations)。 –

+0

是的,你是對的。謝謝。儘管如此,我仍然喜歡我的問題的API設計方面,而Twan的答案下面的滿意同意我的另一個解決方案,這個解決方案是在函數上進行約束,這在其他問題中沒有涉及。 –

回答

2

由於錯誤說,你可以用GADTs做到這一點:

{-# LANGUAGE GADTs, StandaloneDeriving #-} 

data Rating a where 
    Score :: (Fractional a) => a -> Rating a 
    Unscored :: Rating a 

deriving instance (Show a) => Show (Rating a) 
deriving instance (Eq a) => Eq (Rating a) 

-- No need for a context here! 
halfScore :: Rating a -> Rating a 
halfScore (Score a) = Score (a/2) 
halfScore Unscored = Unscored 

StandaloneDeriving是必要的,因爲GHC不能推導出GADTs ShowEq否則。

5

您不應該將Fractional約束放在數據類型上,而應該放在與它一起工作的函數上。所以

data Rating a = Score a | Unscored deriving (Show, Eq) 

makeSomeRating :: Fractional a => a -> Rating a 
makeSomeRating x = Score (x/2) -- can use Fractional functions here 

doSomethingElseWithRating :: Fractional a => Rating a -> Something 
+0

我曾想過這個。我之所以沒有這樣做,是因爲我已經將'分數'與'Rating'類型結合起來了,也就是說,如果你沒有使用'Rating'的衍生工具,那麼使用'Rating'是沒有意義的'分數'(但是使用哪一個取決於用戶...)。這有意義嗎?不知道它是否會更改您的答案... –

+0

@atc,這個答案反映了社區對最佳實踐的一致意見。人們已經嘗試了很多東西,而這通常是最好的方式。 GADT是一種特殊情況,但這不是其中之一。另外,請注意,這個答案作家在Haskell社區的地位遠遠超過了他的SO聲譽得分;-) – dfeuer

+0

我只是想增加一點更多的上下文,當然不打算爭論或暗示Twan是錯的,只是試圖表達我的想法是爲什麼我正在嘗試我的。 –

相關問題