1

Here's my code:

open System

let places = [ ("Grandchester", 552);
               ("Cambridge", 117900);
               ("Prague", 1188126); ]

let statusByPopulation = function
                            | n when n > 1000000 -> "City"
                            | n when n > 50000   -> "Town"
                            | _                  -> "Village"

System.Console.WriteLine ( places |> List.map (fun (_, population) -> statusByPopulation population)) 

let print x = 
    Console.WriteLine (List.map (fun (_, population) -> statusByPopulation population) x) // what I'm trying to do

let something (x:(string * int) list) = 
    List.map (fun (_, population) -> statusByPopulation population) x; // checking what kinf of type it returns

let print: (string * int) list -> unit = 
    Console.WriteLine << List.map (fun (_, population) -> statusByPopulation population) // what I'm not allowed to do

System.Console.ReadKey () |> ignore

I wanted to get familiar with the way the function composition operator worked, but for some reason F# can't find the best possible overload for the function...

In the example where I explicitly state the parameter, it sets the type to be val print : x:('a * int) list -> unit, so I explicitly set the type in the function with the composition operator << hoping I'd get the correct result... I didn't...

I then made the function something with an explicitly declared type for the parameter, just to see what it'd return... It returns this: val something : x:(string * int) list -> string list

So it most definitely returns a type... a list of strings, which I know Console.WriteLine is capable of printing... So why does it tell me it can't determine the overload?

Electric Coffee
  • 11,733
  • 9
  • 70
  • 131
  • Type inference works left-to-right; use pipelining (`|>`) to invoke `Console.WriteLine`. – ildjarn Oct 06 '13 at 22:30
  • I didn't want to use pipelining, I want to save the one parameter... `f << g = f(g(x)) = g(x) |> f` – Electric Coffee Oct 06 '13 at 22:31
  • 3
    I've no idea what you mean. What's wrong with `x |> List.map (fun (_, population) -> statusByPopulation population) |> Console.WriteLine`? (Or more succinctly, `x |> List.map (snd >> statusByPopulation) |> Console.WriteLine`?) – ildjarn Oct 06 '13 at 22:32
  • what's wrong is the fact that long code is hard to maintain why do `let f a = snd a |> someFunc` or `let f a = someFunc(snd a)` when you can do `let f = snd >> someFunc`? – Electric Coffee Oct 06 '13 at 22:38
  • I'm posting a comment rather than answer because I don't want to rationalize; just thought I'd offer a painless workaround. – ildjarn Oct 06 '13 at 22:39
  • the 'painless workaround' doesn't answer the question, I'm aware I can do it your way, but I want to know why my way doesn't work... – Electric Coffee Oct 06 '13 at 22:41
  • 1
    I know it doesn't answer the question, that's why I didn't post an answer. Picky, picky... – ildjarn Oct 06 '13 at 22:42
  • But you're right about the order of evaluation it seems... when I turn it around, putting the `List.map` on the left then do `>>` and then drop the `Console.WriteLine` on the right, it no longer complains... is there an answer as to why it complains, other than it's just the order of evaluation? – Electric Coffee Oct 06 '13 at 22:44
  • 3
    Console.WriteLine has many overloads, type inference works from left to right and top to bottom, so if you put the Console.WriteLine on the left, it don't know which overload you want, when you turn it around, it know you want 'string' – kwingho Oct 07 '13 at 01:05

1 Answers1

4

The type inference in F# works from the left to the right - this means that the compiler uses information available earlier in the program to determine types of expressions later in the program (this is a slight simplification, but it is the general idea).

So in your code, when you write:

Console.WriteLine << List.map (fun (_, population) -> statusByPopulation population)

.. the compiler does not propagate the information about the type of function input through the List.map call back to the WriteLine call. This also explains why forward chaining and composition are generally more useful in F#. The following works:

List.map (fun (_, population) -> statusByPopulation population) >> Console.WriteLine

To get your original code working, you could provide some minimal amount of information that is needed to determine that the right WriteLine overload is the one taking object. If you tell the compiler that it needs to take a list of something, then it can choose the right overload:

(Console.WriteLine:list<_> -> unit) << List.map (fun (_, population) -> 
    statusByPopulation population) 
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553