data LE = forall x. LE [x] ([x] -> String)

[x]

The LE existential is quite similar to the SE existential we have seen earlier. Matching an LE value against the pattern LE xs f gives the list xs and the observation function f . As with SE , we may immediately observe xs by passing it to f . Unlike SE , we may do more observations: find out the length of xs ; transform xs before passing it to f . When transforming xs , we cannot examine the elements, but we may arbitrarily permute, drop and duplicate them. Generally, we may regard xs as a set of distinct `letters': the alphabet Sigma . The permissible transformations then map Sigma to Sigma* -- all strings over the alphabet.

Since we may not in any way examine the elements of the list xs given as the first component of LE , we may only refer to them by their position in xs . We may just as well use the positions (natural numbers) as the stand-in for the elements. One might think, therefore, that LE is isomorphic to the following

data LN = LN Nat ([Nat] -> String)

LN

Nat

Nat

data Nat = Zero | Succ of Nat

LN

LE

[Nat]->String

Nat

n

Nat

[0..n-1]

Size-limited natural numbers are easy to represent. Whereas arbitrary-size Nat s are the least-fixed point of the functor F X = 1 + X , or

data F x = Z | S x

Maybe

n

n

n

F

Empty

Empty

F

Empty

F (F Empty)

Z

S Z

Thus the faithful representation of LE would look like

data L' = Sigma (n::Nat, [F^n Empty] -> String)

LE

L'

data L' a where LZ' :: ([a] -> String) -> L' a LS' :: L' (F a) -> L' a

L'

LZ'

LS'

L'

data L a = LZ ([a] -> String) | LS (L (F a)) type L1 = L Empty

Thus, L1 is the ordinary data type that faithfully represents LE with no junk. We prove it by showing the maps that witness the isomorphism between L1 and LE below. The operations one can do on LE and the operations on L1 also clearly correspond.

iso_LEL1 :: LE -> L1 iso_LEL1 (LE xs f) = go (reverse xs) f [] where -- build the dictionary, the correspondence between x and F^n Empty go :: Eq a => [x] -> ([x] -> String) -> [(a,x)] -> L a go [] f dict = LZ (decode f dict) go (h:t) f dict = LS (go t f ((Z,h):map (\ (a,x) -> (S a,x)) dict)) -- decode according to the dictionary -- By construction, the dictionary contains all a, and so -- the lookup is total. decode :: Eq a => ([x]->String) -> [(a,x)] -> ([a] -> String) decode f dict = f . map (\a -> let Just x = lookup a dict in x) iso_L1LE :: L1 -> LE iso_L1LE = go [] where go :: [a] -> L a -> LE go ps (LZ f) = LE ps f go ps (LS l) = go (Z:map S ps) l

LE

(iso_LEL1 . iso_LEL1) LE

test_eqLE (LE xs1 obs1) (LE xs2 obs2) = zip (map obs1 (tails xs1)) (map obs2 (tails xs2)) test_LE1E = test_eqLE anLE (iso_L1LE . iso_LEL1 $ anLE) where anLE = LE "abc" reverse test_LE1E' = test_eqLE anLE (iso_L1LE . iso_LEL1 $ anLE) where anLE = LE "abc" (drop 1)