简体   繁体   中英

Get constraint in GADT-style declaration without specifically using constructor?

Consider the following:

{-# 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

The constructor Test is declared with an Ord constraint. In comp , I've specifically taken a parameter constructed with Test , which gives the Ord constraint allowing me to use <

Now, suppose I wanted to write:

comp' :: Test a -> Bool
comp' x = (first x) < (second x)

Using the projection functions to get the first and second element. This is not okay because my parameter x was not (necessarily) constructed with Test , so there is no Ord constraint.

So, for my question, is there a way to take the parameter as just x but still somehow have the Ord constraint from the Test constructor, without having to "unpack" or pattern match on the Test constructor or add the constraint to my function?

As for why I would want this, I have a data type with a constructor taking many values, one of which I only need in this particular function, so unpacking it makes my function unnecessarily verbose:

myFunction :: Thing -> ...
myFunction (Thing _ _ _ _ need _ _) ...

As opposed to

myFunction t = ... (need t)

You can define a function which extracts all of the constraints from the constructor:

data Test a where
    Test :: Ord a => { first :: a, second :: a } -> Test a

openTest :: Test a -> (Ord a => r) -> r 
openTest Test{} x = x 

You can then write

comp :: Test a -> Bool
comp x = openTest x $ first x < second x 

but note that the implementation of openTest is completely trivial - and it is less typing to just inline the pattern matching:

comp' :: Test a -> Bool
comp' x@Test{} = first x < second x 

Note also this (ie the Test{} syntax) will work with any constructor, even if it is not a record constructor. Your actual function can simply be expressed as

myFunction t@Thing{} = ... (need t) ...

or equivalently

openThing :: Thing a -> (ThingConstraints a => r) -> r 
openThing Thing{} x = x 

myFunction t = ... (openThing $ need t) ...

Finally, you can also define a function to extract a specific field as well as the constraints which apply to this field, which can be very useful for large constructors with many constraints:

first' :: Test a -> (Ord a => a -> r) -> r 
first' t@Test{} x = x (first t) 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM