Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the benefits of replacing Haskell record with a function

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 ?

like image 405
mb14 Avatar asked Dec 15 '22 18:12

mb14


2 Answers

Well, it's just a simple isomorphism. In ADT algebraic:

IO()String × IO()IntIO()String+Int

The obvious benefit of the RHS is perhaps that it only contains IO() once – DRY FTW.

like image 103
leftaroundabout Avatar answered May 17 '23 03:05

leftaroundabout


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.

  1. 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.

  2. 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.

  3. 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 Processors 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.

like image 33
J. Abrahamson Avatar answered May 17 '23 03:05

J. Abrahamson