I was reading this interesting article about continuations and I discovered this clever trick. Where I would naturally have used a record, the author uses instead a function with a sum type as the first argument.
So for example, instead of doing this
data Processor = Processor { processString :: String -> IO ()
, processInt :: Int -> IO ()
}
processor = Processor (\s -> print $ "Hello "++ s)
(\x -> print $ "value" ++ (show x))
We can do this:
data Arg = ArgString String | ArgInt Int
processor :: Arg -> IO ()
processor (ArgString s) = print "Hello" ++ s
processor (ArgInt x) = print "value" ++ (show x)
Apart from being clever, what are the benefits of it over a simple record ? Is it a common pattern and does it have a name ?
Well, it's just a simple isomorphism. In ADT algebraic:
IO()
String
× IO()
Int
≅ IO()
String
+Int
The obvious benefit of the RHS is perhaps that it only contains IO()
once – DRY FTW.
This is a very loose example but you can see the Arg
method as being an initial encoding and the Processor
method as being a final encoding. They are, as others have noted, of equal power when viewed in many lights; however, there are some differences.
Initial encodings enable us to examine the "commands" being executed. In some sense, it means we've sliced the operation so that the input and the output are separated. This lets us choose many different outputs given the same input.
Final encodings enable us to abstract over implementations more easily. For instance, if we have two values of type Processor
then we can treat them identically even if the two have different effects or achieve their effects by different means. This kind of abstraction is popularized in OO languages.
Initial encodings enable (in some sense) an easier time adding new functions since we just have to add a new branch to the Arg
type. If we had many different ways of building Processor
s then we'd have to update each of these mechanisms.
Honestly, what I've described above is rather stretched. It is the case that Arg
and Processor
fit these patterns somewhat, but they do not do so in such a significant way as to really benefit from the distinction. It may be worth studying more examples if you're interested—a good search term is the "expression problem" which emphasizes the distinction in points (2) and (3) above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With