An idiomatic way of expressing general computation in Haskell

There must be a good idiomatic way to express general computation in Haskell at the type level. All I can think of is an (illegal) imitation of OO.

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 keep it open, so this approach doesn't appeal to me. Am I asking too much?

+3


source to share


2 answers


You can of course do it with the approach you have, you just need to get the syntax and some details, 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 Computation

for Double

which is just plain simple const

. The expression test

should be equivalent (((2^2) * 5) * 0.5) + 100

, and indeed, comparing the two results, I get the same value.

I'm not entirely sure if this is the approach you wanted. It is also not actually equivalent to the method shown in the link you posted, variable expression will be quite tricky with this encoding as there is no good way to feed all variable values ​​to the map to reduce the expression.

+3


source


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

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

      

Then you can:



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*)

      

and so on. You can call this a kind of "computational combinator".

0


source







All Articles