Multiple independent if statements in haskell

Probably a stupid question, but I can't make life understand me.

I want to add to the end of the list based on a series of if statements.

In python (or most other languages ​​I'm familiar with) I could do something like this:

x = ["hi"]
if True:
    x.append("hello")
if not True:
    x.append("wait a minute...")
if True:
    x.append("goodbye")

      

Which would give me:

['hi', 'hello', 'goodbye']

      

How do you achieve such a goal in Haskell?

I can get to:

res :: [[Char]]
res =
    let x = ["Hi"]
    in
        if (True)
            then x ++ ["hello"]
            ... what goes here???
        else x

      

Or do I totally disagree about this?

I am very new to Haskell, so please don't bite ...

+3


source to share


5 answers


idiomatically,



x = concat [ [ "hi" ],
             [ "hello" | True ],
             [ "wait a minute..." | not True ],
             [ "goodbye" | True ] ]

      

+9


source


In Haskell, every expression if

must have a clause else

. In this respect, it is similar to the Python conditional statement:

a if test else b

      

In Haskell, this would be written as:

if test then a else b

      

So how do you write the following in Haskell:

x = ["hi"]
if True:
    x.append("hello")

      

You would do something like:



let x = ["hi"] in
if True then x ++ ["hello"] else x

      

See the offer else

? It just returns the value as is.


However, writing code like this sucks. We want to write code like in Python, and Python is a state. A variable x

is a state. In Haskell, we have a monad State

for writing this kind of code. Consider:

import Control.Monad.State

append a = modify (++ [a])

foo :: [String] -> [String]
foo = execState $ do
    when True       $ append "hello"
    when (not True) $ append "wait a minute..."
    when True       $ append "goodbye"

x = ["hi"]

res = foo x

main = print res

      

Simple right?

+6


source


Haskell differs from how it is done in Python or other languages:

  • The list (and most other data structures) are immutable.
  • if else

    is an expression in Haskell, not an expression seen in other languages. You cannot ignore the part else

    in Haskell like what you did with Python.

Looking at your Python code, it seems what you want to do is if the condition True

is then you want to add an item to the list. This pattern can be abstracted into the following function:

appendIfTrue :: Bool -> a -> [a] -> [a]
appendIfTrue b x xs = if b
                      then xs ++ [x]
                      else xs

      

Once you have written this function, you can achieve the same functionality using the following code:

x = ["hi"]

main = do
  let x1 = appendIfTrue True "hello" x
      x2 = appendIfTrue False "wait a minute" x1
      x3 = appendIfTrue True "goodbye" x2
  print x3

      

It should be noted that here you are creating a new list instead of modifying them as you did in your Python code. Demo video:

λ> main
["hi","hello","goodbye"]

      

+5


source


In addition to @AaditMShah's answer, if you only want to add to the value, without any modification, the writer monad would be the correct abstraction:

import Control.Monad
import Control.Monad.Writer

append :: a -> Writer [a] ()
append = tell . (: [])

x :: [String]
x = execWriter $ do
        tell ["hi"]
        when True $
            append "hello"
        when (not True) $
            append "wait a minute..."
        when True $
            append "goodbye"

      

+5


source


You need to understand that in Haskell you cannot just change x like in your python example. You need to output the value (list of strings) though your convention is where each step is adding or not adding another row. This way you cannot get away with a nested if-then-else statement. You need access to the intermediate list of strings built so far.

So, an elementary operation is to add or not add a string to a list of strings based on a boolean. A function that does this conditional addition might look like this:

condAppend :: Bool -> String -> [String] -> [String]
condAppend c suff pref = if c then pref ++ [suff]
                         else pref

      

No, you just need to chain the condAppends series and apply them to the intermediate list of strings:

res = condAppend True "goodby" 
      $ condAppend False "wait a minute" 
      $ condAppend True "hello" ["Hi"]

      

This gives you:

["Hi","hello","goodby"]

      

Note that the order of execution in res is bottom-to-top (or right-to-left if you write everything on one line) or least-strikeout if you like, as in f (g (h x))

where the left-most function f

is applied last.

If you don't like that, you need an alternative ($) that operates from left to right. You can write your own function (→>) as in

res'= (
       condAppend True "hello"
       >>>
       condAppend False "wait a minute"
       >>>
       condAppend True "goodbye"
      ) ["Hi"]
        where
            f >>> g = g . f

      

and it just so happens that the (much generalized version) (→>) is already defined in Control.Arrow.

+3


source







All Articles