Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forcing let to bind to a function instead of a value

Tags:

f#

I have this piece of code that is supposed to generate random names:

let generateName =
    let rnd = System.Random()
    let c1 = rnd.Next(65, 90) |> System.Convert.ToChar |> string
    let c2 = rnd.Next(65, 90) |> System.Convert.ToChar |> string
    let n = rnd.Next(0, 999)  |> string
    c1 + c2 + n

This evaluates to a string, which isn't quite what i want. Adding a unit-Parameter fixes the problem, but now i have to call my function like a C# function:

let myFunc() =
    "Hello"

myFunc()

Is this how i have to do it, or is there a more idiomatic way?

like image 957
Luca Fülbier Avatar asked Aug 11 '16 20:08

Luca Fülbier


People also ask

What is let in F#?

let is the F# keyword used to bind any value to a name, it's used to bind the so called primitive types such as a string or an integer , to bind to a function or more complex structures such as arrays or records.

How does bind work?

bind() The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

How many arguments does call take?

How many arguments can a function call have? Except for functions with variable-length argument lists, the number of arguments in a function call must be the same as the number of parameters in the function definition. This number can be zero.


2 Answers

TL;DR: () in this context has an entirely different meaning than it has in C#, so do not format it the same way as in C# – use let myFunc () = and myFunc () instead, which better reflects actual semantics and will in turn clarify how you think about and understand the code.


In C#, () means an empty set of parameters (for function/constructor definitions) or arguments (for function/constructor call-sites); i.e., it defines or invokes a nullary function. In F#, () is a literal of type unit, akin to how 42 is a literal of type int; i.e., it is a value in and of itself, just as [] is a value for an empty list. This difference has lots of little implications.

In F#, when you define a let-function with only () for parameters, as in let myFunc() =, you are actually using pattern-matching syntax to define a unary function with a parameter of type unit (not a nullary function!). This particular instance of pattern-matching is so taken for granted that many people do not realize that it's pattern-matching in the first place; but, in fact, defining a function as let myFunc () = rather than as let myFunc (_:unit) = is akin to having let myFunc [] = rather than let myFunc (x:'a list) = (and subsequently asserting that x is empty).

And in F#, when you invoke a let-function with (), as in myFunc (), you are not denoting 'no arguments'; you are in fact supplying an argument of type unit, whose singular value is so often used that it has its own literal syntax: ().

To convince yourself unit is different from 'no parameters/arguments', and is in fact just another parameter/argument like any other, consider the fact that the following is completely legal:

let myFunc() (i:int) (_:unit*unit) = i
let n = myFunc() 42((),())
printfn "%d" n

As you can see, giving unit special whitespace treatment is silly given that it's not special. ;-]

Summarily, since it is usual (necessary?) to separate any named parameter/argument with whitespace, it is then idiomatic to be consistent and do the same with symbolic patterns/values:

let myFunc () =
    "Hello"

myFunc ()
like image 53
ildjarn Avatar answered Sep 29 '22 19:09

ildjarn


Nope, that's how F# works. Every function in F# must have one parameter (functions with multiple parameters are split into multiple functions with one parameter returning an intermediate function), and every function must return something, even if it's unit.

like image 45
kemiller2002 Avatar answered Sep 29 '22 17:09

kemiller2002