Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

if fall through

Tags:

haskell

What's the Haskell equivalent pattern for if fall through in imperative languages, like:

function f (arg, result) {
    if (arg % 2 == 0) {
     result += "a"
    }

    if (arg % 3 == 0) {
     result += "b"
    }

    if (arg % 5 == 0) {
     result += "c"
    }

    return result
}
like image 611
Sawyer Avatar asked May 02 '14 14:05

Sawyer


People also ask

What does if it falls through mean?

if something that has been planned or agreed falls through, it does not happen: The deal fell through when someone made our client a better offer.

What is the sentence of fall through?

Example sentences — Our babysitter fell through so I stayed home with the kids while my wife went to the concert. — The new product launch fell through so I've got to organize it all over again. — Our plans to go to Mexico fell through when my husband had to travel out of town for business.

What is a synonym for fall through?

verbgo under, fail. abort. be lost. break down. collapse.

Is fall through informal?

Informal. To be unsuccessful: choke, fail, fall through.


1 Answers

Instead of using the State monad, you can also use the Writer monad and take advantage of String's Monoid instance (really [a]'s Monoid instance):

import Control.Monad.Writer

f :: Int -> String -> String
f arg result = execWriter $ do
    tell result
    when (arg `mod` 2 == 0) $ tell "a"
    when (arg `mod` 3 == 0) $ tell "b"
    when (arg `mod` 5 == 0) $ tell "c"

Which I think is pretty succinct, clean, and simple.


One advantage this has over the State monad is that you can rearrange order in which concatenations happen by just rearranging the lines. So for example, if you wanted to run f 30 "test" and get out "atestbc", all you have to do is swap the first two lines of the do:

f arg result = execWriter $ do
    when (arg `mod` 2 == 0) $ tell "a"
    tell result
    when (arg `mod` 3 == 0) $ tell "b"
    when (arg `mod` 5 == 0) $ tell "c"

Whereas in the State monad you'd have to change the operation:

f arg = execState $ do
    when (arg `mod` 2 == 0) $ modify ("a" ++)
    when (arg `mod` 3 == 0) $ modify (++ "b")
    when (arg `mod` 5 == 0) $ modify (++ "c")

So instead of having a relationship between execution order and order in the output string, you have to examine the actual operations closely (there's a subtle difference between (++ "a") and ("a" ++)), while the Writer code is very clear at first glance in my opinion.


As @JohnL has pointed out, this is not exactly an efficient solution since concatenation on Haskell Strings is not very fast, but you could pretty easily use Text and Builder to get around this:

{-# LANGUAGE OverloadedStrings #-}
import Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as T
import qualified Data.Text.Lazy.Builder as B
import Control.Monad.Writer

f :: Int -> Text -> Text
f arg result = B.toLazyText . execWriter $ do
    tellText result
    when (arg `mod` 2 == 0) $ tellText "a"
    when (arg `mod` 3 == 0) $ tellText "b"
    when (arg `mod` 5 == 0) $ tellText "c"
    where tellText = tell . B.fromLazyText

And so there's no real change to the algorithm other than conversion to more efficient types.

like image 83
bheklilr Avatar answered Sep 27 '22 22:09

bheklilr