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?
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.
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 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.
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 ()
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With