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 ...
source to share
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?
source to share
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 partelse
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"]
source to share
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"
source to share
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.
source to share