3

If I define a simple function:

let myConcat a b = 
    a + "+" + b

Then given the claim that functions are first-class values in F#, I'd expect to be able to use myConcat like this:

let result = myConcat "a" (fun () -> "b")

Rather than yielding a string "a+b", it gives me the following error:

error FS0002: This function takes too many arguments, or is used in a context where a function is not expected

Hopefully, I'm simply getting the syntax wrong, but it looks to me like functions cannot truly be used as values in F#. Can anyone explain what's going on here?

EDIT To further clarify what I'm asking, I could have the equivalent C# code:

public string myConcat(string a, string b) { return a + "+" + b; }

If I however wanted to pass a "call later function" in param b, I'd have to make it:

public string myConcat(string a, Action<string> b) { return a + "+" + b(); }

Or I could call it thus:

Func<string> b = () => "b";
var result = myConcat("a", b());

C# doesn't (to my knowledge) make the claim that functions are first-class values. F# does make that claim. So what's the difference when I can't treat a unit -> string function as a "lazy evaluated" string value?

David Arno
  • 42,717
  • 16
  • 86
  • 131
  • 3
    Your function doesn't just expect any value as its second argument - it expects a string. A function is not a string. – sepp2k Sep 05 '13 at 21:39
  • If a function that returns a string cannot be treated as a string value, then what does it mean when functions are described as values in F#? – David Arno Sep 05 '13 at 21:57
  • 2
    @DavidArno - From wikipedia (https://en.wikipedia.org/wiki/First-class_function) - " this means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures" – John Palmer Sep 05 '13 at 22:04
  • @DavidArno functions are typed, just like any other object is typed. Just because a function is an object doesn't mean type constraints don't still apply. (fun () -> "b") is of type (unit -> string). – DuckMaestro Sep 05 '13 at 22:06
  • OK, so it appears that functions are values in C# too and so (beyond type inference, which is pretty neat), F# offers nothing C# doesn't when it claims "functions are values". – David Arno Sep 05 '13 at 22:33
  • 2
    @DavidArno functions have been first-class values in C# since 2.0. More info about first-class functions: http://stackoverflow.com/questions/3897543/what-is-the-difference-between-delegates-in-c-sharp-and-functions-as-first-class http://stackoverflow.com/questions/5178068/what-is-a-first-class-citizen-function – Mauricio Scheffer Sep 05 '13 at 23:18
  • @Mauricio, thanks for explaining that. Could you please turn your comment into a more detailed answer, that explains C# treats functions have been first-class values too, so that I can accept it as the "right" answer. Thanks. – David Arno Sep 06 '13 at 16:17
  • @MauricioScheffer Are constructors, properties and methods first-class functions in C#? – J D Sep 09 '13 at 02:02
  • @JonHarrop I know you know this, but here's the answer anyway. It depends on whether you consider explicit eta expansion a limitation for first-class functions. In C# many times you have to explicitly wrap (i.e. eta-expand) methods and property getters in order to pass them around. The F# compiler does this automatically (modulo value restriction and overload ambiguities) for methods and property getters by creating classes that inherit from FSharpFunc and so it looks like you're actually passing a method. – Mauricio Scheffer Sep 09 '13 at 04:07
  • @JonHarrop Only a few times in C# you get to use "method groups", which look exactly like passing a function in F#. Object constructors are a separate case and always have to be eta-expanded explicitly, both in C# and F# (except for discriminated union constructors!). So from this point of view, it boils down to asking whether you need pointfree style to make constructors, property getters and methods first-class functions. IMHO the answer is no. – Mauricio Scheffer Sep 09 '13 at 04:08
  • 1
    @MauricioScheffer: I see. In that case I don't really see a clear distinction between the work you must do to use a constructor, property or method as a function in C# and the work you must do to combine a function pointer and environment in C. So C also has first-class functions? :-) – J D Sep 09 '13 at 11:13
  • @Jon, That was my understanding of what Mauricio was saying. Any language that supports passing a reference to a function as a parameter into another function has "first-class functions". So C does indeed have them. If this is wrong, then I've misunderstood Mauricio. – David Arno Sep 09 '13 at 11:17
  • @JonHarrop Yes, you can pass functions around in C, which means you can have higher-order functions. But you can't create closures (you have to wrap the environment explicitly, as you say), which I consider part of what "first-class function" means. Many share this definition, but ultimately, as noted in http://stackoverflow.com/a/2582804/21239 , there is no precise and generally agreed definition of the term "first class". – Mauricio Scheffer Sep 09 '13 at 13:25
  • That's also why I drew the line at C# 2.0 instead of including previous versions. – Mauricio Scheffer Sep 09 '13 at 14:54

3 Answers3

6

The lambda that you're creating has type (unit -> string). You're then attempting to make a call that comes down to

string -> string -> (unit -> string) -> string

when the expected call should have type

string -> string -> string -> string

So, you're trying to pass in the lambda, when really what you want to pass in is the result of calling the lambda

let result = myConcat "a" ((fun() -> "b")())
Jordan Kaye
  • 2,837
  • 15
  • 15
  • Thanks for your answer. Whilst it didn't directly answer the question, it still helped explain where I was getting confused. Up-voted as a result. – David Arno Sep 12 '13 at 20:52
4

You can you just have to call it this way: let result = myConcat "a" ((fun() -> "b")())

When you created myConcat it gets interpreted as: val myConcat : a:string -> b:string -> string

It's expecting the second argument as a string, and you are passing it a method.

To do what you want you have to declare myConcat as let myConcat a b = a + "+" + b() then yo u can pass in a function as the second argument.

This will then get interpreted as val myConcat : a:string -> b:(unit -> string) -> string

kemiller2002
  • 113,795
  • 27
  • 197
  • 251
  • Thanks for your answer. Whilst it didn't directly answer the question, it still helped explain where I was getting confused. Up-voted as a result. – David Arno Sep 12 '13 at 20:53
2

Thanks mainly to Mauricio Scheffer, I have the answer to my question:

C#, and many other languages, also offer "functions are first-class values". However, being an OO language, in C# this is normally expressed as "functions are first-class objects". The phrase "functions are first-class values" simply refers to the ability to pass a function reference to another function via a parameter as if it were a value. There is no "magic" interpretation of eg unit -> string values as being equivalent to just string values going on, it's just that a unit -> string function is a valid value to pass to another value.

Due to the powerful type-inference features of F#, I don't need to specify the unit -> string type of a referenced function in F# as I would with C#, but the compiler would infer this type and so I wouldn't be able to pass in just a simple string without wrapping it in an anonymous unit -> string function first.

If you understand this stuff better than me and I've got something wrong, please do edit to correct any mistakes.

David Arno
  • 42,717
  • 16
  • 86
  • 131