Term for calling a function with an empty parameter
In functional programming, is it the right name to call a function that takes no arguments? For example:
// 2 types of execute functions
type Function =
| UnitFunction of (unit -> unit)
| OperandFunction of (unit16 -> unit)
let nop () =
()
let shiftRegisterA operand =
registers.A <- registers.A <<< operand
You can nop
call UnitFunction
, and shiftRegisterA
- OperandFunction
?
source to share
What you call here is usually called arity functions. Thus, a function with no arguments will be called a null function and a function with only one argument - a unary function.
While you can still use this terminology in the context of F # functions and be generally accepted, it is not entirely accurate. F # functions always take one argument and return one value because of the defaults being the default.
In your case, nop
has a type unit -> unit
. This means it takes one type argument unit
(the lone value ()
) and returns a type result unit
. This still makes it a unary function.
source to share
In assembly programming, calling a function is the process of pushing parameters on the stack (or register depending on the calling convention) and then jumping to the address of the function.
In higher-level languages like Java, C #, C, etc., this process is more or less hidden to us. However, we see traces of it when we call functions that take no parameters, i.e. void
...
In functional programming languages like F #, haskell, etc. the concept of functions is closer to mathematical functions that give an answer from a single input.
To see that all functions in F # take one input, consider the following function:
// val f: (int*int) -> int
let f (x,y) = x + y
f
takes a couple of integers and produces an integer. A pair from a perspective f
represents a single value that it deconstructs to produce an answer.
// val g: int -> int -> int
let g x y = x + y
This is obviously like a function that takes two integers to create an integer, but if we rewrite it a bit, we can see that this is not necessarily the case:
// val h: int -> int -> int
let h x = fun y -> x + y
h
is equivalent g
, but here we can see that it h
actually takes a single integer, producing a function that takes an integer to create an integer.
->
in the signature is right associative, and we add parentheses, we see more clearly that g
, and h
actually take one input.
// val g: int -> (int -> int)
let g x y = x + y
// val h: int -> (int -> int)
let h x = fun y -> x + y
let gx = g 1 2
let gy = (g 1) 2
let hx = h 1 2
let hy = (h 1) 2
In my opinion, functions in F # have a higher level of abstraction than functions in C # / Java, because C # / Java functions are conceptually closer to assembly language functions than F # functions.
Also, if each function requires one argument, it doesn't make sense for functions that don't take any arguments.
But what about this feature?
// val i: unit -> int
let i () = 3
Doesn't it take arguments to create 3? No, it takes on the value of one ()
, which is just the value in the type unit
.
In Haskell, they have a name for the functions that accept void
and respond:
absurd :: Void -> a
A value can perhaps be thought of as a function that takes no arguments, but I'm not an expert on category theory.
Going back to the sample code:
type Function =
| UnitFunction of (unit -> unit)
| OperandFunction of (unit16 -> unit)
A functional approach would be something like this:
type Abstraction =
| Concrete of obj
| Function of Abstraction -> Abstraction
Those. an Abstraction
is either a value or a function.
Looking at the code, it seems to mimic something that comes close to assembler, so in this case it is fine to think of functions as pushing parameters and jumping to an address.
type Function =
| VoidFunction of (unit -> unit)
| UnaryFunction of (unit16 -> unit)
| BinaryFunction of (unit16 -> unit16 -> unit)
Hope it was interesting.
PS.
It seems like the type unit
is a small detail, but IMO allows a lot of nice things.
- No operators needed.
- Simplifies general programming (case
void
often requires a special case, considerTask<'T>
andTask
). - Allows us to think about functions like math functions instead of going to an address in memory.
source to share