In OOP, we can use an 'interface' to require that an object implement some methods. I think that this can be achieved in FP Haskell, but I am struggling to get a working example.
My goal is to create a toolkit that features a few standardized interfaces to ensure compatibility and standardization within the project domain.
For example, lets say that one of my interfaces defines a calculator, and the methods that it must implement; we can require that each calculator implement and -. There can be many different versions of calculators, and they can can define the functions however they want. Some Haskell-like attempt at code is below.
class Calculator c where
add :: (Float a) => a -> a -> a
sub :: (Float a) => a -> a -> a
instance Calculator DumbCalc where
add a b = 5
sub a b = 3
instance Calculator SmartCalc where
add a b = a b
sub a b = a - b
By using an interface in this way, I can create a modular program that utilizes components without hard coding behavior. In the above example, I could create a program that includes a calculator; in the future I can create a new calculator with more efficient processes, and implement it without any change to the logic in the parent program.
How can I implement this design pattern in Haskell?
CodePudding user response:
If there are only operations and no types, like your example, you would more likely see this as a data type.
data Calculator = Calculator {
add :: Float -> Float -> Float,
sub :: Float -> Float -> Float
}
To construct a calculator:
dumbCalc :: Calculator
dumbCalc = Calculator (\_ _ -> 5) (\_ _ -> 3)
smartCalc :: Calculator
smartCalc = Calculator ( ) (-)
And you can even combine calculators, which is the real secret sauce of modularity.
-- Uses both calculators and returns whichever result is smaller
leastCalc :: Calculator -> Calculator -> Calculator
leastCalc c1 c2 = Calculator (\x y -> min (add c1 x y) (add c2 x y))
(\x y -> min (sub c1 x y) (sub c2 x y))
And functions which need a calculator would take a Calculator
as an argument, which you can pass to them as you please.
Data type modularity like this does not work well if types are being abstracted over in addition to functions. In that case the proper tool is the type class, and as that is generally the point of a type class, I'll just let you read about those in the myriad of available sources online.