-- $answers
-- 1) We have three implementations of @(==)@ for 'UNIT'. All three give
-- the same results because 'UNIT' only has one state. As for the code, I
-- like third implementation the most.
--
-- 2) The 'String' parameter in 'CONS' constructor should, in principle, by
-- a function of type @a@. So there's no need to compare these names --
-- they are the same by construction.
--
-- 3) Generic representations of both @Leaf@ and @[]@ is 'UNIT'. @Leaf ==
-- []@ gives compile-time error :) No matter how you define it. Type of
-- @(==)@ is @a → a@, so comparing 'Bin' with 'List' is illformed.


-- $design-problem
-- There's a design problem with our definition of 'CONS'. It /tries/ to
-- keep the info about constructor names storing them in a 'String' field.
-- This info, however, in only available at runtime. So if we @read@ a
-- @CONS UNIT@, there's no way to verify that constructor name we read
-- actually makes sense. So 'CONS' is OK with both
--
-- > ["CONS", "[]", "UNIT"]
--
-- and
--
-- > ["CONS", "Abrakadabra", "UNIT"]
--
-- So why is this a problem? According to the exercise, 'toList' function
-- __doesn't__ fail! This means that @LEFT (CONS \"Abrakadabra\" UNIT)@
-- must produce a valid list. OK, suppose we neglect the name parameter and
-- just construct an empty list. Then we'll have problems when no generic
-- information is provided. Consider
--
-- > ["[]"]
--
-- vs.
--
-- > ["(:)", "123", "(", "[]", ")"]
--
-- We can distinguish these two cases by looking at number of arguments,
-- right? But what if out type is
--
-- > data Foo = A Int Int | B Int Int
--
-- Now we really __need__ to take the constructor name into account. This,
-- however, is impossible when reading a 'CONS' object, because constructor
-- name is not part of the type and is hence unknown at compile-time. We
-- thus can only trigger an error when converting to @List@. So it should
-- be
--
-- > toList ∷ ListG a → Maybe [a]
--
-- This, however, is not enough. We need to keep track of /all/ possible
-- ways of parsing input. So our generic @read@ function should be
--
-- > read' ∷ [String] → [(a, [String])]
--
-- And because Haskell has support for 'ReadS', let's make it
--
-- > read' ∷ String → [(a, String)]
--
-- With these modifications we can now implement everything :)
--
-- /P.S./ I think this also answers the \"Reflection\" questions.


-- | Converts a list to its generic representation. This function is
-- inverse of 'toList'. Constructor for unit list is called @"[]"@, for
-- composite list -- @"(:)"@.
fromList ∷ [a] → ListG a
fromList []     = LEFT  . CONS "[]"  $ UNIT
fromList (x:xs) =
fromList ∷ [a] → ListG a
fromList []     = LEFT  . CONS "[]"  $ UNIT
fromList []     = LEFT  . CONS "[]"  $ UNIT