I'm trying to build a string from optional arguments. For example to generate a greeting string from a title and a name This is trivial in a imperative language and would look like this
def greeting(title, name):
s = "Hello"
if a :
s += "Mr"
if b:
s += b
My first attempt in haskell is :
greeting :: Bool-> Maybe String -> String
greeting title name = foldl (++) "Hello" (catMaybes [title' title, name])
where
title' True = Just "Mr"
title' False = Nothing
I'm sure there is a bette way to do it. First, I'm sure this foldl
catMaybes
combination exists somewhere but I couldn't find it. Second, folding works here because I'm using the same operation (and the same type). So what is there a better way to do it ?
I was also thinking using a Writer but I'm not sure either how to do it.
This is only an example (maybe bad or too simple). My question is more , how to generalize it to many arguments .
So the real problem is not just about concatening 2 strings but more how to generate letters from a template with optional sections and parameters, like you would do in Word with the mail merge tool.
You have on one a csv file with customer name, telephone number, how much is overdue etc. Some of the field could be optional. On the other side you have a template and the goal is to generate one letter per customer (row) according to the *template. The question is then how do you write this template in haskell (without the help of any templating library). In my first example the template will be "hello (Mr){name}" but in the real word the template could be in invoice, a statement letter or even a complete accounting report.
Haskell is so good at abstractions, it can easily replicate the imperative patterns. What you're doing in your example is called a "builder" pattern. Writer
is a monad, which wraps a pattern of accumulation or "building" of any Monoid
data, and String
is a Monoid
.
import Control.Monad.Writer hiding (forM_)
import Data.Foldable
greeting :: Bool -> Maybe String -> String -> String
greeting mr name surname =
execWriter $ do
tell $ "Hello,"
when mr $ tell $ " Mr."
forM_ name $ \s -> tell $ " " ++ s
tell $ " " ++ surname
tell $ "!"
main = do
putStrLn $ greeting False (Just "Ray") "Charles"
putStrLn $ greeting True Nothing "Bean"
Outputs:
Hello, Ray Charles!
Hello, Mr. Bean!
that's the
forM
I was looking for. Thank you very much.