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

?

+3


source to share


2 answers


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.

+9


source


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, consider Task<'T>

    and Task

    ).
  • Allows us to think about functions like math functions instead of going to an address in memory.
+2


source







All Articles