Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is List.reduce necessary in this newbie example?

Tags:

f#

Playing around with F#, I am confused with the following behavior. When List.reduce (>>) is commented out, there is the error

defaultLabel |> showRainbow
----------------^^^^^^^^^^^
This expression was expected to have type
    CoolLabel -> 'a    
but here has type
    (CoolLabel -> CoolLabel) list

in this example boiled down from http://fsharpforfunandprofit.com/posts/conciseness-functions-as-building-blocks/ :

// create an underlying type
type CoolLabel = {
    label : string; 
}    

let defaultLabel = 
    {label="";}

let setLabel msg label = 
   {label with CoolLabel.label = msg}

let rainbow =
    ["red";"orange";"yellow";"green";"blue";"indigo";"violet"]

let showRainbow = 
    rainbow
    |> List.map setLabel 
    |> List.reduce (>>)

// test the showRainbow function
defaultLabel |> showRainbow

When List.reduce (>>) is removed, I would think that showRainbow should return a list of CoolLabel, and the compiler would be cool with everything.

Edit -> (Ignore this sentence, because the answer below changed my understanding.): "As an aside, I get that List.reduce (>>) would return the last CoolLabel from the list."

Thanks.

like image 205
Snorex Avatar asked Sep 01 '13 01:09

Snorex


1 Answers

Removing the line with List.reduce (>>) changes the type of showRainbow and so you get something that is not a function and so the pipelining operator cannot call it with defaultLabel as an argument.

In the original program, the type of showRainbow is a function that turns one CoolLabel into another:

val showRainbow : (CoolLabel -> CoolLabel)

If you remove the line, you get a list of functions:

val showRainbow : (CoolLabel -> CoolLabel) list 

This example is not using functions in a trivial way, so let me explain a bit what is acutally going on. We start with rainbow which is a list of colors. Next, rainbow |> List.map setLabel turns the list of colors into a list of functions. You can read this as:

rainbow |> List.map (fun color -> setLabel color)

But, setLabel takes two arguments. Here we specify just the first one, so the result is a function that expects CoolLabel and changes its color to the current color of the rainbow.

Once you have the list of functions, List.reduce (>>) composes them - it builds a new function that calls all of them on the input it receives. So the result is essentially a function:

let resultingFunction input = 
  setLabel "violet" (setLabel "indigo" (setLabel "blue" ( ... (input)))))

Now you can see why this returns a label with violet - it changes the color of defaultLabel to red, then to orange, then to yellow, etc. and finally to indigo and then to violet!

If you change setLabel so that it does not ignore the original color, but perhaps combines them (by appending strings) then you will see how the setLabel function was called on all colors:

let setLabel msg label = 
  { label with CoolLabel.label = label.label + " " + msg }

The result will be:

> defaultLabel |> showRainbow;;
val it : CoolLabel = {label = " red orange yellow green blue indigo violet";}
like image 60
Tomas Petricek Avatar answered Oct 19 '22 00:10

Tomas Petricek