我試圖用以下定義對泛型Range
類型建模:
data Range a = Range
{ lower :: a
, upper :: a }
我添加了一個智能構造函數(并且只導出了它而沒有數據構造函數),這樣消費者就不會意外地創建一個Range
,其lower
字段大于upper
字段:
range :: Ord a => a -> a -> Range a
range lower upper =
if lower > upper
then Range upper lower
else Range lower upper
這一切都很好,我繼續推導它的Functor
實例:
instance Functor Range where
fmap f (Range lower upper) = Range (f lower) (f upper)
這成了問題,因為我可以:
main :: IO ()
main = do
let r1 = range 1 2
r2 = fmap negate r1
return ()
-- Now r2 has its upper field less than its lower field
我天真的嘗試是在fmap
的實現中使用智能構造函數range
,如:
instance Functor Range where
fmap f (Range lower upper) = range (f lower) (f upper)
但是,它不會編譯,因為f lower
和f upper
不受約束,只有一個Ord a
實例。
如何修復這個問題,從而保持lower
始終小于或等于upper
的類型不變量?
正如在評論中提到的,沒有辦法使它成為合法的函子,因為
fmap f
必須適用于所有函數f
,即使結果類型沒有Ord
實例。在我看來,你有兩個很好的選擇:
首先:不要定義函子約束,使用更受限制的類型創建一個新函數
rangeMap
(set就是這樣做的)。第二,更改范圍的表示,使其支持
Functor
實例,但將lower
和higher
實現為需要Ord
約束的函數。根據您的用例,這兩種方法中的任何一種都可能更可取。