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 l
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!
I know it's an old post ... but it may be useful ...
in a pure "functional" way ...
greeting :: Maybe String -> String -> String
greeting Nothing name = "Hello" ++ ", " ++ name
greeting (Just title) name = "Hello" ++ ", " ++ title ++ " " ++ name ++ "!"
how to call :
-- greeting Nothing "John"
-- greeting Nothing "Charlotte"
-- greeting (Just "Mr.") "John"
-- greeting (Just "Miss.") "Charlotte"
You could avoid using a Maybe
for the title and do:
greeting :: Bool-> Maybe String -> String
greeting title name = "Hello" ++ title' ++ (maybe "" id name)
where title' = if title then "Mr" else ""
If you have a number of Maybe
s you could use mconcat
since String
is a monoid:
import Data.Monoid
import Data.Maybe
greeting :: [Maybe String] -> String
greeting l = fromJust $ mconcat ((Just "Hello"):l)
Actually Haskell is smarter than any imperative approach.
Let's imagine name
has a value Nothing
. Does it make sense to render something like "Hello Mr"
? Probably it would make more sense to consider it as an exceptional situation, and for that we can again use Maybe
. So first of all, I'd update the signature of the function to the following:
greeting :: Bool -> Maybe String -> Maybe String
Now we can look at our problem again and find out that Haskell provides multiple ways to approach it.
Maybe
is a monad, so we can use the do
syntax with it:
greeting mr name = do
nameValue <- name
return $ if mr
then "Hello, Mr. " ++ nameValue
else "Hello, " ++ nameValue
Maybe
is also a functor, so alternatively we can use fmap
:
greeting mr name =
if mr
then fmap ("Hello, Mr. " ++) name
else fmap ("Hello, " ++) name
We can also do a bit of refactoring, if we consider the signature as the following:
greeting :: Bool -> (Maybe String -> Maybe String)
i.e., as a function of one argument, which returns another function. So the implementaion:
greeting mr =
if mr
then fmap ("Hello, Mr. " ++)
else fmap ("Hello, " ++)
or
greeting True = fmap ("Hello, Mr. " ++)
greeting False = fmap ("Hello " ++)
if you find this syntax better.
I think that your function is violating the SRP (Single responsibility principle). It is doing two things:
Mr
You can notice that the Bool
and String
(name) refer to the same "entity" (a person).
So let's define a person:
data Person
= Mister String
| Person String
deriving (Eq)
We can now create an instance of show
that makes sense:
instance Show Person where
show (Mister name) = "Mr. " ++ name
show (Person name) = name
and finally we can reformulate your greeting function as:
greeting :: Maybe Person -> String
greeting (Just person) = "Hello, " ++ show person
greeting Nothing = "Hello"
Live demo
The code is simple, readable and just few lines longer (don't be afraid of writing code). The two actions (prefixing and greeting) are separated and everything is much simpler.
If you really have to, you can trivially create a function to generate a Mister
or a Person
based on a boolean value:
makePerson :: Bool -> String -> Person
makePerson True = Mister
makePerson False = Person
Live demo