在汇编程序中调用一个函数是在堆栈上推送参数(或根据调用约定寄存器)然后跳转到函数地址的过程。
在更多高级语言中,例如Java,C#,C等等,这个过程或多或少都隐藏在我们的面前。但是,当我们调用不带参数的函数时,我们会看到它的痕迹,即void
。在函数式编程语言(如F#,haskell等)中,函数的概念更接近于从单个输入产生答案的数学函数。
地看到,在F#的所有函数接受单个输入让我们来看看下面的函数:
// val f: (int*int) -> int
let f (x,y) = x + y
f
接受一对整数,并产生一个整数。这两个人从f
的角度来看,它解构了一个单一的价值来产生答案。
// val g: int -> int -> int
let g x y = x + y
显然,这似乎是一个接受两个整数从而产生一个整数的功能,但如果我们把它改写略有我们看到事实并非如此:
// val h: int -> int -> int
let h x = fun y -> x + y
h
相当于g
但在这里我们看到h
实际上只接收一个整数,该整数产生一个接受整数来产生整数的函数。
签名中的->
是正确的联想,我们添加了更多的偏见,我们更清楚地看到g
和h
实际上只是一个输入。
// 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
在F#我看来,功能比功能的更高层次的抽象的说,C#/ Java作为C#/ Java的功能在概念上更接近比F#的功能是汇编语言的功能。另外,如果每个函数都需要一个参数,那么对于不接受参数的函数来说,这是没有意义的。
但是这个功能呢?
// val i: unit -> int
let i() = 3
它不接受没有参数产生3?不,它接受单位价值()
,这只是unit
类型中的价值。
在Haskell他们有一个名称,接受void
,并产生一个答案功能:
absurd :: Void -> a
值或许可以看作是不带参数的函数,但我不是一个范畴论专家。
再回到示例代码:
type Function =
| UnitFunction of (unit -> unit)
| OperandFunction of (unit16 -> unit)
功能的方法将是这样的:
type Abstraction =
| Concrete of obj
| Function of Abstraction -> Abstraction
Ie的Abstraction
是一个值或一个函数。
看看代码,它似乎模仿了看起来接近汇编语言的东西,所以在这种情况下,可以将函数看作是推送参数并跳转到地址。
type Function =
| VoidFunction of (unit -> unit)
| UnaryFunction of (unit16 -> unit)
| BinaryFunction of (unit16 -> unit16 -> unit)
希望这很有趣。
PS。
看起来好像unit
类型是一个小细节,但IMO使许多好东西。
- 无需声明。
- 简化通用编程(
void
案例通常需要特殊情况,考虑Task<'T>
和Task
)。
- 允许我们思考数学函数等功能,而不是跳转到内存中的地址。
回复:特殊的'void'情况下,C#中最震撼的一个是'Action'和'Func'类型之间的分割。 – scrwtp