Type for representing a list with 0 to 5 values
Well, a recursive solution is certainly the normal and in fact nice thing in Haskell, but it's a bit tricky to limit the number of elements then. So, for a simple solution to the problem, first consider the stupid-but-working one given by bradm.
With the recursive solution, the trick is to pass a “counter” variable down the recursion, and then disable consing more elements when you reach the max allowed. This can be done nicely with a GADT:
{-# LANGUAGE GADTs, DataKinds, KindSignatures, TypeInType, StandaloneDeriving #-}
import Data.Kind
import GHC.TypeLits
infixr 5 :#
data ListMax :: Nat -> Type -> Type where
Nil :: ListMax n a
(:#) :: a -> ListMax n a -> ListMax (n+1) a
deriving instance (Show a) => Show (ListMax n a)
Then
*Main> 0:#1:#2:#Nil :: ListMax 5 Int
0 :# (1 :# (2 :# Nil))
*Main> 0:#1:#2:#3:#4:#5:#6:#Nil :: ListMax 5 Int
<interactive>:13:16: error:
• Couldn't match type ‘1’ with ‘0’
Expected type: ListMax 0 Int
Actual type: ListMax (0 + 1) Int
• In the second argument of ‘(:#)’, namely ‘5 :# 6 :# Nil’
In the second argument of ‘(:#)’, namely ‘4 :# 5 :# 6 :# Nil’
In the second argument of ‘(:#)’, namely ‘3 :# 4 :# 5 :# 6 :# Nil’
For the sake of completeness, let me add an "ugly" alternative approach, which is however rather basic.
Recall that Maybe a
is a type whose values are of the form Nothing
or Just x
for some x :: a
.
Hence, by reinterpreting the values above, we can regard Maybe a
as a "restricted list type" where lists can have either zero or one element.
Now, (a, Maybe a)
simply adds one more element, so it is a "list type" where lists can have one ((x1, Nothing)
) or two ((x1, Just x2)
) elements.
Therefore, Maybe (a, Maybe a)
is a "list type" where lists can have zero (Nothing
), one (Just (x1, Nothing)
), or two ((Just (x1, Just x2)
) elements.
You should now be able to understand how to proceed. Let me stress again that this is not a convenient solution to use, but it is (IMO) a nice exercise to understand it anyway.
Using some advanced features of Haskell, we can generalize the above using a type family:
type family List (n :: Nat) (a :: Type) :: Type where
List 0 a = ()
List n a = Maybe (a, List (n-1) a)
I won’t answer your exercise for you — for exercises, it’s better to figure out the answer yourself — but here’s a hint which should lead you to the answer: you can define a list with 0 to 2 elements as
data List a = None | One a | Two a a
Now, think about how can you extend this to five elements.