user9993
user9993

Reputation: 6170

Type mismatch with printfn

I have a function that concatenates two strings and puts a comma and space between them and returns that. I can't print the result though.

let concat first second =
    first + ", " + second


printfn "%s" concat "hello" "world"

Type mismatch. Expecting a 'string -> 'a -> 'b -> 'c' but given a 'string -> unit' The type ''a -> 'b -> 'c' does not match the type 'unit' (using external F# compiler)

This expression was expected to have type 'string' but here has type 'string -> string -> string'

How do I properly print the returned string?

Edit: It seems that I needed brackets surrounding the call to concat. Why was this needed?

let concat first second =
    first + ", " + second


printfn "%s" (concat "hello" "world")

Upvotes: 2

Views: 566

Answers (2)

rmunn
rmunn

Reputation: 36678

Robert Nielsen's answer is correct, but let me try a different way to explain it, because if you're new to F# it might be hard to understand an expression like:

(((printfn "%s") concat) "hello") "world"

So think of it this way: in F#, functions can be passed as input to other functions. For example, you might pass your concat function as input to List.reduce:

List.reduce concat ["one"; "two"; "three"]
// Returns "one, two, three"

Now, there are two ways you could read this. Function calls could have left precedence, or right precedence. I.e., if function calls had right precedence, then the ["one"; "two"; "three"] list would be considered the first argument to the concat function (because it's rightmost and thus has precedence). Or if function calls have left precedence, then that list would be the second argument to the List.reduce function, and the concat function would be the first argument. F# makes the latter (left precedence) the default: in the absence of parentheses, everything after a function's name is considered to be an argument to that function. So the following:

printfn "%s" concat "hello" "world"

is read as: "call the printfn function with four parameters: a string, a function, and two more strings". If you want the strings "hello" and "world" to be arguments to concat, you have to add parentheses so that F# will resolve the concat call with higher precedence (as you've already discovered):

printfn "%s" (concat "hello" "world")

This is read as: "call the concat function with two strings as parameters, and then take the result of that function call and pass it as the second parameter to printfn".

But think about this: if the default was the other way around, and function calls had right precedence — how would you put parentheses into that List.reduce example to make it work the way you want? What you want is "Call List.reduce with two parameters: the first one is a function, and the second one is a list of strings." But how would you add parentheses to this:

List.reduce concat ["one"; "two"; "three"]

to get that result? Well, this would work:

(List.reduce concat) ["one"; "two"; "three"]

But in my opinion, that would be confusing. To understand that expression, you would have to understand how F# currying works, and that's a concept that usually takes a little while to wrap your head around. Whereas with left-precedence function calls, the expression someFunction a b c always means "call someFunction with three parameters", no matter whether any of those three parameters are functions.

I hope this long-winded explanation has helped you understand F# function calls a little better.

Upvotes: 5

Robert Nielsen
Robert Nielsen

Reputation: 515

Because of F-sharp operator precedence: Symbol and Operator Reference.

Edit: You can think of it as turning

(((printfn "%s") concat) "hello") "world"

into

(printfn "%s") ((concat "hello") "world")

but sprinkled with some type inference.

Upvotes: 3

Related Questions