What does this '()' notation mean?
I just started to learn F#. The book uses the following notation:
let name() = 3
name()
what that differs from this:
let name = 3
name
?
Solution 1:
Before answering what ()
is lets get some basics defined and some examples done.
In F# a let statement has a name, zero or more arguments, and an expression.
To keep this simple we will go with:
If there are no arguments then the let statement is a value.
If there are arguments then the let statement is a function.
For a value, the result of the expression is evaluated only once and bound to the identifier; it is immutable.
For a function, the expression is evaluated each time the function is called.
So this value
let a = System.DateTime.Now;;
will always have the time when it is first evaluated or later invoked, i.e.
a;;
val it : System.DateTime = 1/10/2017 8:16:16 AM ...
a;;
val it : System.DateTime = 1/10/2017 8:16:16 AM ...
a;;
val it : System.DateTime = 1/10/2017 8:16:16 AM ...
and this function
let b () = System.DateTime.Now;;
will always have a new time each time it is evaluated, i.e.
b ();;
val it : System.DateTime = 1/10/2017 8:18:41 AM ...
b ();;
val it : System.DateTime = 1/10/2017 8:18:49 AM ...
b ();;
val it : System.DateTime = 1/10/2017 8:20:32 AM ...
Now to explain what ()
means. Notice that System.DateTime.Now
needs no arguments to work.
How do we create a function when the expression needs no arguments?
Every argument has to have a type, so F# has the unit type for functions that need no arguments and the only value for the unit type is ()
.
So this is a function with one argument x
of type int
let c x = x + 1;;
and this is a function with one argument ()
of type unit
let b () = System.DateTime.Now;;
Solution 2:
Definitely do NOT think of ()
as some syntax for a function call or anything like that. It's just a value, like 3, 5, 'q', false, or "blah". It happens to be a value of type Unit
, and in fact it's the only value of type unit, but really that's beside the point. ()
here is just a value. I can't stress that enough.
First consider
let name x = 3
What's this? This just defines a function on x, where x can be any type. In C# that would be:
int Name<T>(T x)
{
return 3;
}
Now if we look at let name () = 3
(and I somewhat recommend putting that extra space there, so it makes ()
look more a value than some syntactic structure) then in C# you can think of it as something like (pseudocode)
int Name<T>(T x) where T == Unit //since "()" is the only possible value of Unit
{
return 3;
}
or, more simply
int Name(Unit x)
{
return 3;
}
So we see that all let name () = 3
is, the definition of a function that takes a Unit
argument, and returns 3, just like the C# version above.
However if we look at let name = 3
then that's just a variable definition, just like var name = 3
in C#.
Solution 3:
In
let name() = 3
name()
name
is a function, of type unit -> int
.
In
let name = 3
name
name
is an integer, of type int
.
In F#, every function has an input type and an output type. The input type of let name() = 3
is unit
, which has only one value ()
. Its output type is int
, which has values from –2,147,483,648
to 2,147,483,647
. As another example type bool
has only two values, true
and false
.
So back to you question what's the usage of ()
. If you don't specify the input value of a function, it cannot get executed. So you have to specify an input value to your function let name()=3
to get it executed and because of its input type is unit
, the only value you can use is ()
.
Here is another way to define the name function:
let name : (unit -> int) = (fun _ -> 3);;
and compare this to:
let name : int = 3
Solution 4:
Using ()
creates a function which takes a paramter of type unit
, rather than the second case which is just a simple integer.
This is particularly important when you want to control execution of the function.
The main difference is when you have
let name() =
printfn "hello"
1
vs
let name =
printfn "hello"
1
then
let t = name + name
will print "hello" once. But
let t = (name()) + (name())
will print "hello" twice.
You have to be careful with this when considering the order in which functions are evaluated.
Consider the following program:
let intversion =
printfn "creating integer constant"
1
printfn "integer created"
let funcversion() =
printfn "executing function"
1
printfn "function created"
let a = intversion + intversion
printfn "integer calculation done"
let b = (funcversion()) + (funcveriosn())
printfn "function calculation done"
This will print the following in order
- creating integer constant
- integer created
- function created
- integer calculation done
- executing function
- executing function
- function calculation done