F#奇妙游(27):大大的小小let

小小的let

let,三个字符,一开始就要用,学到computation expression,居然还会发现let别有洞天,值得一探,真是奇妙!

最初刚开始学习F#时,对let的理解是,定义一个变量,或者定义一个函数。但从语法的角度看,结合当时的学习内容,这个理解也是没有问题的。

let x = 10
let f x = x + x

printfn "%A" x
printfn "%A" (f 10)

在帮助文档中,"let所声明的实体的作用域限制在包含当前代码的域,仅限于申明之后的部分。"这样来看,可以理解为let在当前作用域例引入一个名称。F#是严格按照顺序的语言,所以let的作用域就是从let开始,到当前作用域结束。

问题从哪里开始出现的呢?

let与模式匹配

大概从模式匹配这里开始,let的语法就变得复杂了。

let (x, y) = (10, 20)
let (x, y) = (10, 20) in x + y

帮助里也说,let的语法是:

let identifier-or-pattern [ : type ] = expressionbody-expression
let identifier parameter-list [ : return type ] = expressionbody-expression

这是所谓绑定的语法,第一个是把值绑定到一个标识符或者模式,第二个是把值(函数)绑定到标识符。这在C/C++里面都不是什么问题,申明变量,开辟一个内存,把这个内存的引用放在标识符里,把值放在内存里,这样就完成了绑定。

但是在函数式编程中,这个语法的含义就不那么直接。特别是,这里还用到标识符或者模式匹配。

  1. 这个函数式编程实际上是多范式的!(没错)所以可以引用地址、绑定值也是没问题的!(可能是错的)
  2. 这里的let另有含义。

而在大多数编程语言中,都有一个很重要的概念:叫做作用域。这里到底是不是作用域的概念?如果是,那么let就是定义一个全局变量,或者局部变量。

let的函数式理解

那么let本质上到底是什么呢?为什么要问这个问题?

在看到MSDN中有一段关于verbose syntax的页面,里面有一个关键词in,在构成let的语法中,in是可选的。那么,in到底是什么呢?

假设我们有以下一段代码,我们把in加上去后,看起来是这样的。

let x = 10 in 
    let y = 100 in 
        let f z = x + y + z in
            f 1

加上in之后,这段代码的含义就变得更加清晰了,可以称之为nested let绑定。

从语义上来看,我们在printfn "%d" (f 1)中绑定了f,而f的定义中,又绑定了xy。这里的in就是一个作用域的概念,f的作用域是xy的作用域的子集。

那么如果我们要追求纯函数,去掉in,这段代码应该怎么写呢?当然是三层匿名函数,分别绑定xyz

let fx x =
    let fy y =
        let fz z = x + y + z
        fz 1
    fy 100
f1 10

或者,利用匿名函数,写成这样:

(fun x ->
    (fun y ->
        (fun z -> x + y + z) 1) 100) 10

或者改成管道操作符,就是这样的。

10 |> fun x ->
    100 |> fun y ->
        1 
        |> (fun z -> x + y + z)

这段代码唯一的难点就是|>,其他的含义十分明白。刚好就是把let的绑定替换成对应匿名函数的参数绑定。

let identifier-or-pattern [ : type ] = expressionbody-expression

变为

expressionbody-expression |> fun identifier-or-pattern [ : type ] ->

这个函数的函数体,实际上就是letin之后的表达式,直到其作用域结束。

结论

  1. 函数式编程的学习中,因为语言的语法设计,会有很多不同的理解,这些理解都是正确的,但是有些理解更加贴近语言的设计者的意图。
  2. let的可以理解为是一个匿名函数调用,let的作用域是这个函数的函数体。
  3. 苟日新,日日新,学习每天都开心。
posted @ 2023-09-05 09:00  大福是小强  阅读(18)  评论(0编辑  收藏  举报  来源