user1850254
user1850254

Reputation: 2471

Restricting the number of elements a data constructor can have

I am writing an ADT in Haskell which represents a hand of cards. My problem is that I would like to restrict the number of (Suit,Face) which represents a card to 5 which represents a hand.

data Card = Hand [(Suit,Face)]

What I tried to do was this but it does not work.

data Card = Hand [(Suit,Face),(Suit,Face),(Suit,Face),(Suit,Face),(Suit,Face)]

My question is: how do i restrict the number of tuples to 5?

Upvotes: 8

Views: 169

Answers (3)

Petr
Petr

Reputation: 63359

I'd like to add that if you use a solution based on 5-tuples (as suggested in the other answers), you can still have all folding/traversing functions you need. In particular, first define

import Control.Applicative
import Data.Foldable
import Data.Traversable

data Tuple5 a = Tuple5 a a a a a

and define folding and traversing operations on it:

instance Traversable Tuple5 where
    traverse f (Tuple5 a b c d e)
            = Tuple5 <$> f a <*> f b <*> f c <*> f d <*> f e
instance Foldable Tuple5 where
    foldMap = foldMapDefault
instance Functor Tuple5 where
    fmap    = fmapDefault

Then, you can have

data Hand = Hand (Tuple5 Card)

and fold/traverse the structure using any methods from Foldable/Traversable/Functor.


Update: Recently I created a small library tuples-homogenous-h98 that defines newtype aliases for homogenous tuples such as

newtype Tuple5 a = Tuple5 { untuple5 :: (a,a,a,a,a) }

and adds proper Traversable, Foldable, Functor, Applicative and Monad instances.

Upvotes: 14

gspr
gspr

Reputation: 11227

How about

data Card = Hand (Suit, Face) (Suit, Face) (Suit, Face) (Suit, Face) (Suit, Face)

?

Or, if you really want to use a list (I mean, if you wanted 15-card hands, my suggestion becomes very silly), you can keep data Card = Hand [(Suit, Face)] and then tell users to not use the constructor, and instead provide a "smart constructor" like

fromList :: [(Suit, Face]) -> Maybe Card
fromList xs 
        | length xs == 5 = Just (Hand xs)
        | otherwise      = Nothing

You could then also leave the constructor itself available with a warning like "use only if you guarantee that the supplied list contains precisely 5 elements".

By the way: Isn't naming the data type Card and the constructor Hand a bit counterintuitive? I think the data type should be called Hand in your case. Card is a nice alias for a Suit-Face pair, so you could do type Card = (Suit, Face) and data Hand = Hand [Card].

Upvotes: 5

Lee
Lee

Reputation: 144126

You could create a type for Cards and a Hand type with five Card elements:

type Card = (Suit, Face)
data Hand = Hand Card Card Card Card Card

Upvotes: 5

Related Questions