3

There must exist a good idiomatic way to express general computations in Haskell on type level. All I can come up with is this (illegal) OO imitation.

class Computation where
  compute :: Computation -> Double -> Double

data Id = Id
instance Computation Id where 
  compute _ = id

data Square a = Computation a => Square a 
instance Computation (Square a) where 
  compute (Square underlying) x = sqr $ compute underlying x where square x = x*x

data Scale a = Computation a => Scale a Double
  compute (Scale underlying c) x = c * compute underlying x

Ideally, I would like to retain openness, so this approach doesn't appeal to me. Am I asking for too much?

NioBium
  • 583
  • 3
  • 10

2 Answers2

3

You can certainly do it with the approach you have, you just need to get the syntax and some of the details right, but this certainly works:

class Computation a where
    compute :: a -> Double

instance Computation Double where
    compute = id

data Square a = Square a

instance Computation a => Computation (Square a) where
    compute (Square underlying) = square $ compute underlying where square i = i * i

data Scale a = Scale a Double

instance Computation a => Computation (Scale a) where
    compute (Scale underlying c) = c * compute underlying

data Add a = Add a Double

instance Computation a => Computation (Add a) where
    compute (Add underlying c) = c + compute underlying

test :: Add (Scale (Scale (Square Double)))
test = Add (Scale (Scale (Square 2) 5) 0.5) 100

main :: IO ()
main = print $ compute test

Note that I had to add an instance of Computation for Double, which is just simply const. The test expression should be equivalent to (((2^2) * 5) * 0.5) + 100, and indeed comparing these two results I get the same value.

I'm not entirely sure this is the approach that you wanted, though. This also isn't really equivalent to the method shown in the link you posted, expressing variables would be pretty difficult with this encoding as there's no good way to feed in a map of all variable values to reduce the expression.

bheklilr
  • 53,530
  • 6
  • 107
  • 163
  • You could alternatively do `data Square a where Square :: Computation a => a -> Square a`, depending on whether you want to `Square` anything that's not a `Computation`. – dfeuer Aug 12 '14 at 05:36
0

It depends on what you want to do with computations, but one idiomatic way is this:

data Computation = Computation { compute :: Double -> Double }

Then you can have:

idCmp :: Computation
idCmp = Computation id

squareCmp :: Computation
squareCmp = Computation (\i -> i * i)

composeCmp :: Computation -> Computation -> Computation
composeCmp b a = Computation (compute b . compute a)

scaleCmp :: Double -> Computation
scaleCmp r = Computation (r*)

etc. You could call this a kind of "computation combinators".

Dominique Devriese
  • 2,998
  • 1
  • 15
  • 21