How to chain the use of maybe argument in haskell?

前端 未结 5 1726
渐次进展
渐次进展 2021-01-16 09:06

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

相关标签:
5条回答
  • 2021-01-16 09:21

    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!
    
    0 讨论(0)
  • 2021-01-16 09:23

    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"
    
    0 讨论(0)
  • 2021-01-16 09:33

    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 Maybes 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)
    
    0 讨论(0)
  • 2021-01-16 09:35

    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.

    Hello, Monads

    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
    

    Hello, Functors

    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.

    0 讨论(0)
  • 2021-01-16 09:41

    I think that your function is violating the SRP (Single responsibility principle). It is doing two things:

    • prefixing the name with Mr
    • rendering the greeting message

    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

    0 讨论(0)
提交回复
热议问题