Haskell/Solutions/Higher-order functions

Choosing how to compare
Instead of comparing the two strings directly, we compare the all uppercase version. The order of the original two strings is then based on the order of the uppercase versions.

Higher-Order Functions and Types
Part 1:

1. A for loop in Haskell:

0 1 2
 * Main> for 0 (<3) (+1) (print)

2.  is an   action built recursively. At the beginning of each recursive step, the boolean  is checked. The base case, reached when  is , the result is the do-nothing action,. In the recursive case, the result is an action consisting of  followed by   called with   instead of   and with the same function arguments as before.

Part 2:

1. The naïve solution,, would not work. The type of is , a list of actions. There is nothing wrong with that per se, as actions are Haskell values like any others. However, a list of actions is not an action, and so it cannot e.g. be put in an  do-block and executed through.

2.

Or using :

3.

Alternatively,  can be implemented in terms of  :

Or point-free:

Function manipulation
1.

A stylistic alternative is using lambdas to emphasise that a function is being returned. If you ever get confused while trying to implement a function-returning function, doing that might make it easier to see what the implementation should be.

2.
 * passes the elements of a pair to, in order. As   always returns its first argument,   returns the first element of the pair; in other words, it is   in disguise.
 * converts, which returns the first element of a pair, into a function of two arguments which returns the first one; therefore,   is equivalent to.
 * Alternatively,  and   are inverses (that is, they undo each other;   amounts to  ), and so if   is   it is evident that   is.
 * A third approach is noting that, given the types of  and   type of   is an  function. The only possible function with this type is  ; therefore,   must be equivalent to it.
 * , given a pair, produces a pair with swapped elements., therefore, takes two arguments and pair them in swapped order; it is equivalent to.

All of the above answers can be rigorously checked by expanding and substituting definitions. Here is how it is done in the first case; we suggest you try it with the other two.

3. ''What follows is a meticulous step-by-step explanation. If you are here because you gave up the exercise, you might still enjoy skipping to the end and trying to understand what the implementation is doing before returning and seeing how it was built.''

There are two key insights needed to pull off this trick. The first one is remembering that a left fold

can be expanded as

and then realising that if we flip  and swap its arguments everywhere

we get an expression that associates to the right, just like a right fold!

The only problem is that the list elements in our right-associative expression are in the wrong order. The obvious way of fixing that is reversing the input list. That leads us to the first solution:

Reversing the list, however, is rather inelegant and takes time proportional to the length of the list. At this point, the second insight comes into play. By looking at the expression with the nested  and squinting a little

we can see we are actually taking  and applying several functions in sequence to it. We can make that transparent by rewriting the expression using.

If we switch the  to prefix notation

it becomes clear that the function at the left of  can be written as a fold with  :

Another way of thinking about this step is recovering the intuition that  takes a list and replaces   with the binary operator and   with the initial accumulator. In our case,, the do-nothing dummy function, is used as the initial accumulator. Next, we factor out the repeated  using  :

Here we meet the same problem: the list elements are not in the order we would like them to be. The reversal is consistent with the fact that functions composed with  are applied from right to left. However, this time we can, instead of reversing the list, simply flip :

Merely flipping the fold operator is not enough to turn  into   because in the general case the operator arguments have different types, and so flipping causes a mismatch. Folding a list with, however, can only work if the list elements are  functions, and  functions can be composed in both directions.

By abstracting from the concrete example we get the second solution:

If you find this final expression hard to grasp, making it more pointful by removing the s may help:

Alternatively, we could make it even more pointfree, though that would be rather excessive.