It is often useful, especially for recursive data types with multiple constructors, to define a catamorphism, a way to fold up a data structure when given one function for each of the possible constructors.
For example, for Bool
the catamorphism is
bool :: a -> a -> Bool -> a
bool x _ False = x
bool _ y True = y
and for Either
it is
either :: (a -> c) -> (b -> c) -> Either a b -> c
either f _ (Left x) = f x
either _ g (Right x) = g x
A more advanced catamorphism is the one for lists, which you have probably seen before: foldr
!
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f init [] = init
foldr f init (x:xs) = f x (foldr f init xs)
We don't usually think of it this way (or at least I don't), but foldr
is a catamorphism: it handles pattern-matching and recursively deconstructing the list for you, so long as you provide "handlers" for values found in the two constructors of [a]
:
- One case to handle
[]
, needing no arguments at all: just a value of type b
- One case to handle
(x:xs)
. This case takes one argument representing x
, the head of the list, and one argument representing the result of folding up the tail recursively, a value of type b
.
A catamorphism is less exciting for a type with only one constructor, but we can easily define one for your Keypress
type:
key :: (Int -> Char -> a) -> Keypress -> a
key f (Keypress x c) = f x c
In a way, the catamorphism allows you to abstract away the pattern-matching part of the function definition, after which you can just work with functions that don't need to touch the underlying data type directly anymore.
Having defined that generally-useful function once, you can use it many times, to implement any point-free function your heart desires. In your case, you could simply write
getSeq :: Keypress -> [Char]
getSeq = key replicate