Range我正在尝试使用以下定义对泛型类型进行建模:
data Range a = Range
{ lower :: a
, upper :: a }
Run Code Online (Sandbox Code Playgroud)
我添加了一个智能构造函数(并且仅在没有数据构造函数的情况下导出它),以便消费者不会意外创建一个Range其lower字段大于其upper字段的 a :
range :: Ord a => a -> a -> Range a
range lower upper =
if lower > upper
then Range upper lower
else Range lower upper
Run Code Online (Sandbox Code Playgroud)
这一切都很好,我继续派生Functor它的一个实例:
instance Functor Range where
fmap f (Range lower upper) = Range (f lower) (f upper)
Run Code Online (Sandbox Code Playgroud)
这变得有问题,因为我可以这样做:
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
Run Code Online (Sandbox Code Playgroud)
我天真的尝试是range在实现中使用智能构造函数fmap,例如:
instance Functor Range where
fmap f (Range lower upper) = range (f lower) (f upper)
Run Code Online (Sandbox Code Playgroud)
但是,这不会编译为f lower并且f upper不限于具有Ord a实例。
lower我该如何解决这个问题,以便我可以保持始终小于或等于的类型不变性upper?
正如评论中提到的,无法使其成为合法函子,因为即使结果类型没有实例,它也必须fmap f适用于所有函数。fOrd
在我看来,你有两个不错的选择:
\n第一:不要定义函子约束,创建一个rangeMap具有更严格类型的新函数(这就是set 的做法)。
data Range a = Range { lower :: a, higher :: a } deriving Show\n\nrange :: Ord a => a -> a -> Range a\nrange lower upper =\n if lower > upper\n then Range upper lower\n else Range lower upper\n\nrangeMap :: Ord b => (a -> b) -> Range a -> Range b\nrangeMap f (Range x1 x2) = range (f x1) (f x2)\nRun Code Online (Sandbox Code Playgroud)\nrangeMap (negate) $ range 2 1 \nRange {lower = -2, higher = -1}\nRun Code Online (Sandbox Code Playgroud)\n其次,更改范围的表示形式,使其支持Functor实例,但将lower和实现higher为需要Ord约束的函数。
data Range a = Range a a\n\ninstance Functor Range where\n fmap f (Range x1 x2) = Range (f x1) (f x2)\n\nlower :: Ord a => Range a -> a\nlower (Range x1 x2) = min x1 x2\n\nhigher :: Ord a => Range a -> a\nhigher (Range x1 x2) = max x1 x2\nRun Code Online (Sandbox Code Playgroud)\n\xce\xbb:r = negate <$> Range 1 2\n\xce\xbb:lower r\n-2\n\xce\xbb:higher r\n-1\nRun Code Online (Sandbox Code Playgroud)\n根据您的用例,这两种方法中的任何一种可能更可取。
\n| 归档时间: |
|
| 查看次数: |
174 次 |
| 最近记录: |