[Haskell-cafe] Haskell-Cafe Digest, Vol 106, Issue 38

John Lato jwlato at gmail.com
Wed Jun 27 02:51:10 CEST 2012


> Message: 12
> Date: Wed, 27 Jun 2012 00:19:30 +0200
> From: Tillmann Rendel <rendel at informatik.uni-marburg.de>
> Subject: Re: [Haskell-cafe] Martin Odersky on "What's wrong with
>        Monads"
> Cc: haskell-cafe at haskell.org
> Message-ID: <4FEA3572.5060200 at informatik.uni-marburg.de>
> Content-Type: text/plain; charset=ISO-8859-1; format=flowed
>
> Hi,
>
> MightyByte wrote:
>> Of course every line of your program that uses a Foo will change if you switch
>> to IO Foo instead.
>
> But we often have to also change lines that don't use Foo at all. For
> example, here is the type of binary trees of integers:
>
>   data Tree = Leaf Integer | Branch (Tree Integer) (Tree Integer)
>
> A function to add up all integers in a tree:
>
>   amount:: Tree -> Integer
>   amount (Leaf x) = x
>   amount (Branch t1 t2) = amountt1 + amountt2
>
> All fine so far. Now, consider the following additional requirement: "If
> the command-line flag --multiply is set, the function amount computes
> the product instead of the sum."
>
> In a language with implicit side effects, it is easy to implement this.
> We just change the third line of the amount function to check whether to
> call (+) or (*). In particular, we would not touch the other two lines.
>
> How would you implement this requirement in Haskell without changing the
> line "amount (Leaf x) = x"?

Why would you do that even in an imperative language?  The program
logic turns into a spaghetti mess, and it's much harder to test.

I would write Tree like this:

data Tree a = Leaf a | Branch (Tree a) ( Tree a)
  deriving (Foldable, Show)

and instead of an "amount" function I would use a fold.  If you like,
you can use "ala" from Control.Newtype

*Main Data.Foldable Data.Monoid> let t1 = Branch (Leaf 1) (Branch
(Leaf 4) (Leaf 5)) :: Tree Int
*Main Data.Foldable Data.Monoid> ala Sum foldMap t1
10
*Main Data.Foldable Data.Monoid> ala Product foldMap t1
20

Now the amount calculation can be something like

amount :: Num a => Tree a -> IO a
amount tree = multFlag >>= \b -> if b then ala Product foldMap tree
else ala Sum foldMap tree


although I probably wouldn't actually write it unless it was called in
more than one place.  There are other ways to write it too; the
important part is that checking the configuration is completely
separate from the tree traversal.

Plus, if it changes again (now there's another flag that says to
ignore values == n or something, you can use the same fold, just
change the function that's passed to it (or the monoid instance if
you're using that).

Plus, this type of code is much simpler to debug, test, and maintain
than imperative-style magic functions.

Cheers,
John



More information about the Haskell-Cafe mailing list