The shape of the score (linear sections, repeated sections, alternative endings), is encoded in the type. That the Show and Functor instance were easy to code indicates that the it might actually be useful. Previously when I've tried type level programming I've ended up with structures that I couldn't actually work with.
{-# LANGUAGE GADTs #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE TypeOperators #-}
module GADTSection7 where
data Z = Z
data x :. xs = x :. xs
data TLinear
data TRepeat
data TAltRep
data TList shape e where
Nil :: TList Z e
Linear :: e -> TList shape e -> TList (TLinear :. shape) e
Repeat :: e -> TList shape e -> TList (TRepeat :. shape) e
AltRep :: e -> [e] -> TList shape e -> TList (TAltRep :. shape) e
-- Good - shape of the list reflected in its type...
tune0 = Linear "...o..o" $ Nil
tune1 = Linear "....o..o" $ Linear "...o..o" $ Nil
tune2 = Repeat "o.o..o.o" $ Nil
-- Good - disallows junk...
-- tune3 = Linear "....o..o" $ Linear 3000 $ Nil
sameType :: TList sh e -> TList sh e -> Bool
sameType _ _ = True
-- Good - throws error...
-- > sametype tune1 tune2
tune4 = Repeat "........" $ Nil
demo01 = sameType tune2 tune4
tune5 = Linear "o.o....." $ Nil
-- Good ...
-- sameType enforces sameShape not just sameLength
-- demo02 = sameType tune4 tune5
sameShape :: TList sh e -> TList sh e' -> Bool
sameShape _ _ = True
tune6 = Linear 3000 $ Nil
demo03 = sameShape tune5 tune6
instance Functor (TList shape) where
fmap _ Nil = Nil
fmap f (Linear e xs) = Linear (f e) (fmap f xs)
fmap f (Repeat e xs) = Repeat (f e) (fmap f xs)
fmap f (AltRep e es xs) = AltRep (f e) (map f es) (fmap f xs)
instance Show e => Show (TList shape e) where
showsPrec _ Nil = showString "Nil"
showsPrec _ (Linear e xs) =
showString "Linear" . spS . shows e . spS . appS . spS . shows xs
showsPrec _ (Repeat e xs) =
showString "Repeat" . spS . shows e . spS . appS . spS . shows xs
showsPrec _ (AltRep e es xs) =
showString "AltRep" . spS . shows e . spS . showList es . spS
. appS . spS . shows xs
tune7 = AltRep "o.o.o..o" ["o.", ".o"] $ Nil
spS :: ShowS
spS = showChar ' '
appS :: ShowS
appS = showChar '$'