I'm working through Swierstra's 2008 paper. I'm up to Section 3 eval addExample, just before 'Automating injections'. And boy! does it need automating.
Is it just me, or is the whole thing over-engineered? Compare this in the paper
addExample :: Expr (Val :+: Add) addExample = In (Inr (Add (In (Inl (Val 118))) (In (Inl (Val 1219)))))
With this made-up example
addExample3 :: Expr (CoPr (CoPr Val Add) (CoPr Add Mul))
addExample3 = In (Inl (Inr (Add (In (Inl (Inl (Val 333))))
(In (Inr (Inl (Add (In (Inl (Inl (Val 303))))
(In (Inl (Inl (Val 330))))
) ) ) )
) ) )
(Using type constructor CoPr instead of :+: in the paper. I've used layout there just to make sure I'm balancing my parens. This is as bad as LISP.)
eval addExample3 -- ===> 966 [correct]
So it works to have type constructor/functor Add appearing twice in the Expr. There's something wrong in the design here, because the CoProduct just needs to be a set of functors. Rather than using Swierstra's :+: I could use a type-level list(?) I notice the references don't include the HList paper, even though that was some 4 years earlier.
For each 'atomic' type constructor (Val, Add) there needs to be both a Functor instance, which is essentially determined from its arity; and an Eval instance, which makes it do something useful; so later in the paper there's a Render instance for each functor, to do something else useful.
For addExample and addExample3 above, GHC can infer the type. But when it gets to the smart (so-called) constructors, you need to supply the CoProduct type of the Expr. Can't those constructors (there's one for each atomic constructor) build the Expr type as it goes? (If the needed type constructor already appears, use Inl/Inr to get to it; if it doesn't appear, append it to the end of the CoProduct and generate the Inl/r.)
(I started looking at a-la-Carte because it's claimed the approach doesn't fit with Overlapping Instances. Just not so: it was easy to adapt it to use overlaps in Hugs. That's the least of the difficulties.)
:+:type constructor would support arbitrary nesting and repetition of functors within anExpr/evalAlgebratype. But Swierstra doesn't need that richness, and Section 4 uses a much simpler structure. Never the less theaddExamplehas a pile-up of data constructors to build an abstract syntax tree of 3 nodes. It looks like a structural impedance mismatch between the Algebra and the AST. AFAICT from the paper, anevalAlgebrais a set of functors; an adequate representation would be a type-level list(?) – AntC Apr 27 '19 at 02:47