请考虑以下事项:
{-# LANGUAGE GADTs, GADTSyntax #-}
data Test a where
Test :: Ord a => { first :: a, second :: a } -> Test a
comp :: Test a -> Bool
comp (Test fst snd) = fst < snd
构造函数Test
以Ord
约束声明。在comp
中,我专门采用了Test
构造的参数,该参数提供Ord
约束,允许我使用<
现在,假设我想写:
comp' :: Test a -> Bool
comp' x = (first x) < (second x)
使用投影函数获取第一个和第二个元素。这不没关系,因为我的参数x
没有(必然)用Test
构建,因此没有Ord
约束。
因此,对于我的问题,有没有办法将参数仅作为x
,但仍然以某种方式从Ord
构造函数获得Test
约束,而不必“解包”或Test
构造函数上的模式匹配或将约束添加到我的函数?
至于为什么我想要这个,我有一个带有构造函数的数据类型,它带有很多值,其中一个我只需要在这个特定的函数中,所以解压缩它会使我的函数不必要地冗长:
myFunction :: Thing -> ...
myFunction (Thing _ _ _ _ need _ _) ...
与
相反myFunction t = ... (need t)
答案 0 :(得分:1)
您可以定义一个从构造函数中提取所有约束的函数:
data Test a where
Test :: Ord a => { first :: a, second :: a } -> Test a
openTest :: Test a -> (Ord a => r) -> r
openTest Test{} x = x
然后你可以写
comp :: Test a -> Bool
comp x = openTest x $ first x < second x
但请注意openTest
的实现完全是微不足道的 - 只是内联模式匹配的输入更少:
comp' :: Test a -> Bool
comp' x@Test{} = first x < second x
另请注意,这(即Test{}
语法)将适用于任何构造函数,即使它不是记录构造函数。您的实际功能可以简单地表示为
myFunction t@Thing{} = ... (need t) ...
或等效
openThing :: Thing a -> (ThingConstraints a => r) -> r
openThing Thing{} x = x
myFunction t = ... (openThing $ need t) ...
最后,您还可以定义一个函数来提取特定字段以及适用于此字段的约束,这对于具有许多约束的大型构造函数非常有用:
first' :: Test a -> (Ord a => a -> r) -> r
first' t@Test{} x = x (first t)