mdo fib cond (n .< 2) 1 (fib(n-1) + fib(n-2)) return fib

fib n = if n < 2 then 1 else fib(n-1) + fib(n-2)

fib(n-1)

n

Int

n

n

M Int

M (App (Func (FuncNo 0)) [App (FPrimOp I_Sub) [Arg 0, Con (ILit 1)]])

-

Int

M Int

<

<

(Ord a) => a -> a -> Bool

.<

M Bool

<

<

<

M Bool

Num

Bool

b -> b not :: b -> b instance Boolean Bool where false = False true = True (&&) = (P.&&) (||) = (P.||) not = P.not

(&&), (||)

not

Bool

Bool

Eq

class (Boolean b) => Eq a b | a -> b where (==), (/=) :: a -> a -> b x /= y = not (x == y)

Eq

instance Eq Int Bool where (==) = (P.==) (/=) = (P./=) instance Eq Double Bool where (==) = (P.==) (/=) = (P./=)

M

instance Eq (M Int) (M Bool) where (==) = binOp I_EQ (/=) = binOp I_NE

Ord

class (Eq a b) => Ord a b | a -> b where (), (>=) :: a -> a -> b

if

cond

if

cond

cond

M Bool -> M Int -> M Int -> M Int

class (Boolean b) => Cond a b | a -> b where cond :: b -> a -> a -> a

instance Cond Int Bool where cond x y z = if x then y else z instance Cond (M Int) (M Bool) where cond = terOp I_Cond instance Cond (M Bool) (M Bool) where cond = terOp I_Cond terOp op (M c) (M t) (M e) = M $ App (FPrimOp op) [c, t, e]

M Bool

instance Boolean (M Bool) where false = M $ Con $ LInt 0 true = M $ Con $ LInt 1 x && y = cond x y false x || y = cond x true y not x = cond x false true

Int

M Int

cond (n < 2) 1 (fib(n-1) + fib(n-2))

module MyPrelude(module Prelude, Boolean(..), Eq(..), Ord(..), Cond(..)) where import qualified Prelude as P import Prelude hiding (Eq(..), Ord(..), (&&), (||), not) ...

module M where import Prelude() import MyPrelude ...

In my last post I had a little DSL embedded in Haskell. Defining the Fibonacci function looked like this:Compare that with ordinary Haskell:Why do they look different? Could I make my DSL look exactly like Haskell? First, lets look at the parts that look the same, like. In Haskell, withof type, this just an expression and it has some value (depending on). In the DSL, withof typethe value of this expression is in fact an abstract syntax tree, namely (something like). The reason we can make the same expression mean different things depending on the type is that numerical operators likeare overloaded, and so they do different things forand. So what about, can we overload that too? Theoperator is already overloaded and has type. But theoperator in the DSL returns, so there's no way we can use; the return type is fixed. What to do? Well, there's nothing sacred with the operator, it's just another name, but defined in the Prelude. We can make our own if we hide the Prelude. So let's do that, but carefully. We want to be able to still useas before, and for ourreturn type. So we need to overload the return type as well as the argument type.Before we do that, let's define a class that is analogous to, but for Boolean values. We'll need this class soon. Thetype has a few important functions and values, let's put those in the class.First we import the Prelude explicitly, this allows us to hide some things we want to redefine. The we define the Boolean class, which gives new meaning to some prelude functions. Finally, we make an instance saying that the new, andbehave as the old ones for good oldfashioned. After this definition we can use the Boolean operators just as before, they are just overloaded now, but forthey resolve to their old meaning.So back to comparison,first.The new(we need to hide the Prelude one) is now more general; the return type is overloaded as well. Too avoid ambiguities we have a functional dependency. The type of the values we compare will determine the type of the Boolean result. (This isn't necessary, but without this we might need many type annotations.) Nothing is a member of this class, so we need to add the types we need. We really should all types that have equality, but let's do two samples.And since the whole point of this was to allow special equality for ourvalues, let's look at that too.A new (simplified)looks similar, both the class and the instances.So now for the next challange. The Haskell Fibonacci function uses, and the DSL uses a special function. Well, now we are stuck. These is no way at all to overload the specialsyntax in Haskell; it's wired in. This could be viewed as a design mistake in Haskell, but so it is. So could we use thefunction in the Haskell version too? Well, not the original, it has type. We need to overload that too.Again, we add a functional dependency. And again, it will save some ambiguities. The obvious instances:Oh, and the instance forthat I skipped before.After this effort we can write the body of the Fibonacci function the same way for both typeand type, namelyAnd what about all these new classes and instances. how do we package them to make them easy to use?We hide the stuff we want to override, and re-export the rest of the Prelude. Any module that wants to use this new Prelude now has to say:After this all regular Haskell code works as before, but we can also take advantage of the new overloadings. Enough for today.

Labels: Haskell, overloading