[Haskell-cafe] Type unions
Lennart Augustsson
lennart at augustsson.net
Sat May 10 10:15:10 EDT 2008
Try making a type class for the functions. That will allow you both varargs
and unions.
Have a look at Text.Printf.
-- Lennart
On Sat, May 10, 2008 at 1:28 PM, Eric Stansifer <eric.stansifer at gmail.com>
wrote:
> I have been trying to write a DSL for Povray (see www.povray.org) in
> Haskell, using the technique of:
> http://okmij.org/ftp/papers/tagless-final-APLAS.pdf
> with some inspiration taken from
> http://okmij.org/ftp/Haskell/DSLSharing.hs
>
> The Povray Scene Description Language is a very declarative language,
> with few high level constructs (even loops take a bit of work) --
> which is why I'm putting it in Haskell.
>
> At one point, I needed a "varargs" function for the DSL, a function f
> :: b -> a -> b dressed up to take a variable number of 'a's, known at
> compile time. This was easy enough:
>
> > data Nil a = Nil
> > data Cons b a = a ::: b a
> > infixr 1 :::
> >
> > class VarArgs v where
> > apply_args :: (s -> a -> s) -> s -> v a -> s
> >
> > instance VarArgs Nil where
> > apply_args _ start _ = start
> >
> > instance VarArgs b => VarArgs (Cons b) where
> > apply_args f start (a ::: b) = apply_args f (f start a) b
>
> The solution is quite workable: I can simply write the following, and
> I believe the summation is expanded out at compile-time:
>
> > apply_args (+) 0 (2 ::: 3 ::: 8 ::: 1 ::: (-3) ::: Nil)
>
> But I found I also needed a function to take a union type -- that is,
> the function would either take an argument of type T1, or of type T2,
> known at compile time. I tried a similar technique as I tried with
> varargs, and unfortunately ended up with this:
>
> > data LeftOf a b = L a
> > data RightOf a b = R b
> >
> > class Union u where
> > apply_union :: (a -> c) -> (b -> c) -> (u a b) -> c
> >
> > instance Union LeftOf where
> > apply_union f _ (L a) = f a
> >
> > instance Union RightOf where
> > apply_union _ g (R b) = g b
> >
> > type A = Integer
> > type B = String
> > type C = ()
> >
> > type Union_ABC u1 u2 = u1 A (u2 B C)
> >
> > f_A = show . (+ 3)
> > f_B = reverse
> > f_C = const "unit"
> >
> > f :: (Union u1, Union u2) => Union_ABC u1 u2 -> String
> > f = apply_union f_A (apply_union f_B f_C)
> >
> > main = do
> > putStrLn $ f $ (L 6 :: Union_ABC LeftOne LeftOne)
> > putStrLn $ f $ R (L "hello, world")
> > putStrLn $ f $ R (R ())
>
> Notice a lot of ugliness in my example: e.g., the definition of f,
> the type signature of f (I can't move the context into the
> type-synonym Union_ABC), creating objects of the union type, and the
> unpleasant surprise that I needed to provide the type of 'L 6'. This
> solution is very not-scalable: the Povray SDL is a "messy" language,
> and for my DSL I would need approximately 20 or 30 such unions, each a
> union of about 20 types (admittedly with a lot of overlap from union
> to union).
>
> I think the solution is to abandon the lofty ideal of statically
> determining argument types; instead have a universal type with tags
> to distinguish types dynamically:
>
> > data Universal = UA A | UB B | UC C
> > f :: Universal -> String
> > f (UA a) = f_A a
> > f (UB b) = f_B b
> > f (UC c) = f_C c
> >
> > main2 = do
> > putStrLn $ f $ UA 6
> > putStrLn $ f $ UB "hello, world"
> > putStrLn $ f $ UC ()
>
> ...but I'm not ready to give up hope yet. Suggestions please?
>
> Eric
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/haskell-cafe/attachments/20080510/222e7918/attachment.htm
More information about the Haskell-Cafe
mailing list