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