Haskell Use guarded equation to create type

Viewed 281

I want to create a type called TwoValued and it contains two values: a and b, and I'd like to make sure that a <= b

data TwoValued a b | a <= b = ValuePair a b
                   | otherwise = ValuePair b a

But it's not working, I think guarded equation is not allowed here, so is there any other way to achieve what I want? Thanks.

3 Answers

You are needing here a ADT (Abstract data type), so, first, create your data type correctly:

data TwoValued a = ValuePair a a deriving Show

then make the constructor

makePair :: Ord a => a -> a -> TwoValued a a
makePair x y = if x < y then (ValuePair x y) else (ValuePair y x)

Finally, export the module with just the function the user need to see (in this case makePair)

As far as I know, you can not define logic in a constructor. To put it bold, a constructor is used to discriminate between values, and to hold parameters.

You can however hide the constructor(s), and define functions to perform the construction. For example:

module Foo(TwoValued(), twoValued, firstValue, secondValue)

data TwoValued a = ValuePair { firstValue :: a, secondValue :: a}

twoValued :: Ord a => a -> a -> TwoValued a
twoValued a b | a <= b = ValuePair a b
              | otherwise = ValuePair b a

So we define a data structure TwoValued and export it without the constructor (we write module Foo(TwoValued()), note module Foo(TwoValued(..))) as well as a function twoValued that acts like a constructor.

We can also export the getters firstValue :: TwoValued a -> a and secondValue :: TwoValued a -> a such that other modules can obtain the parameters.

Smart constructors are the idiomatic approach, as others already commented.

As an alternative, we can also use "smart pattern-constructors", which allow logic beyond regular ones.

{-# LANGUAGE PatternSynonyms #-}

module SmartCons(OrdPair(OrdPair)) where

data OrdPair = OP Int Int

pattern OrdPair :: Int -> Int -> OrdPair
pattern OrdPair x y <- OP x y where
   OrdPair x y = if x <= y then OP x y else OP y x

We can then use them as intended:

> import SmartCons
> case OrdPair 3 2 of OrdPair x _ -> x
2

Note that the OP dumb constructor is accessible only within the module. From outside, we can only use the smart one.

Whether this is a good or bad idea is arguable, though. I guess we are not yet used to expect constructors to involve some logic. Hiding an expensive computation behind construction could easily surprise the user.

Related