Control structures

Tip

Learning goal

In this chapter we will revisit the basic control structures that we have covered in the previous chapters, and we will introduce some new ones.

Reading material

You have already covered the basic control-structures in previous chapters:

  • functions
  • let..in
  • if-then-else
  • case expressions
  • Pattern matching
  • function guards

Let’s introduce some new ones:

  • “Map and filters” section from LYAH - not the entire chapter, just read from this section onwards
  • “Functional programming” chapter from RWH
    • This chapter has a section explaining the difference between foldl and foldl'. Don’t worry if you do not understand it yet. Just remember – always use foldl' instead of foldl. The latter is a trap left by the Haskell zen-masters for unsuspecting newbies. (Same for foldr' vs foldr)

Exercises

General practice

Go back through all your previous solutions and see if they can be shorterned by using map, filter, foldl', foldr'. Do you still miss your for loops?

Where is my loop index?

Write a function that accepts lines-of-code, represented as a list of Strings, and return a new list where each line-of-code is pre-fixed with the line number?

prefixLineNumbers :: [String] -> [String]

-- prefixLineNumbers
--  [ "module Main where"
--  , ""
--  , "increment :: Int -> Int"
--  , "increment x = x + 1"
--  ]
--
-- OUTPUT:
-- [ "1: module Main where"
-- , "2: "
-- , "3: increment :: Int -> Int"
-- , "4: increment x = x + 1" ]
  • First write it using only functions and recursion and if-then-else.
  • Write it using map. Is it possible? If not, why?
  • Write it using foldl'
  • Write it using zip
  • Write it using zipWith

Do you still miss your loop indices – i, j or idx?

“let” vs “where”

let and where look deceptively similar. Can you go back over all your past solutions and think of scenarios where you could have interchaged a let with a where? Are there any scenarios where it was not possible to interchange them?

Todo

any good article that explains the difference between let & where?

Expressions vs. statements

Try writing an if expression without an else branch. Is it possible in Haskell? Is it possible in other languages that you are familiar with? Spend as much time as you need in understanding this. This is how Haskell is very different from any other imperative language that you might be familiar with. It’s a “there-is-no-spoon moment” once you eventually get it. Need more clues?

  • What is the difference between expressions and statements in C and Java? Are you writing statements, or expressions in Haskell?
  • Can you define a function in Haskell that has only let bindings? Why / why not?

Exhaustiveness-checking in pattern matches

Go back to any of your previous solutions, where you were doing a pattern-match on a sum-type. Remove one of patterns in the case match. Does your editor complain about anything?

Warning

In case your editor doesn’t complain, exit your ghci session and restart it with stack ghci --ghci-options="-Wall". Now load your file and see if ghci complains about something.

Try running your function such that it receives a value for the pattern-match that you have just removed. For example, in the following code (from the previous RLE exercise), comment-out the NoRepetition i pattern match and try running your function with an an input of "abcdef". What happens?

case rleUnit of
  Repetition c i -> _something
  NoRepetition c -> _something -- remove this

Have you now started appreciating why ADTs are a super-power?

Stuff that you might struggle with

Thinking “functionally” vs “imperatively”

You will probably have the logic for most of the exercises in your head, but you might struggle with actually writing the logic using only map, fold, or zip. Do not worry - this is expected. There is a bit of re-wiring that your brain needs to undergo, to start thinking functionally. That’s the whole point of this chapter. Do NOT skip the exercises.

Is there a function to do X ?

If you’ve been spending time on the API-documentation (which you should be doing), you would have noticed that map, fold and zip are not the only “control structures” that are available. There seem to be dozens of variatons of map, fold and zip for common use-cases (in fact that is true for other things as well, for example take a look at maybe and fromMaybe functions). Initially, you will end-up re-implementing a control-structure that is already present in the standard library. A lot of the code that you will initially write can be reduced to a one-liner by using the correct function/control-structure from the standard library. This will come only with practice and experience. Do not be afraid to go back and refactor your code when you learn of something new. Refactoring is a super-power in Haskell - if you mess-up something in refactoring the compiler is going to shout at you till you fix it. So, don’t be afraid to refactor.

What the hell is going on? Can I print the value of this variable?

TODO