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?
source to share
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.
source to share
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".
source to share