目录:网上冲浪指南

姗姗来迟的 F# 4.7 新特性介绍

2020/03/06

2019 年 9 月 23 日,F# 4.7 发布,这次更新为 F# 语法加入了更多的甜味。

隐式 yield

在之前的版本中定义 seq 块的时候,我们必须给每个返回值的前面加上 yield 关键字。F# 4.7 改进了这一点,在 seq 块中再也不必每行起手就是一个 yield 了。

// F# 4.6 and lower
let nums =
    seq {
        yield 1
        yield 2
        yield 3
    }

// F# 4.7 and higher
let nums' =
    seq {
        1
        2
        3
    }

除了 seq 之外,任何支持 YieldCombineDelayZero 的 CE(Computation Expression)中的 yield 也可以隐式的使用。

但这并不是隐式 yeild 的全部,List、Array 的字面量定义中也可以使用隐式 yield 这一特性!再过去,如果你想要在列表的中通过判断某个条件来生成一个值,你就需要为列表中的每一个元素的加上 yield 前缀,而现在,这些重复的关键字终于可以移除掉了。

// F# 4.6 and lower
let f includeWeekend =
    [
      yield "Monday"
      yield "Tuesday"
      yield "Wednesday"
      yield "Thursday"
      yield "Friday"
      if includeWeekend then
         yield "Saturday"
         yield "Sunday"
    ]

// F# 4.7 and higher
let f' includeWeekend =
    [
        "Monday"
        "Tuesday"
        "Wednesday"
        "Thursday"
        "Friday"
        if includeWeekend then
            "Saturday"
            "Sunday"
    ]

这对于那些使用 F# 列表创建 HTML 的 DSL 非常有用,例如 Fable、Giraffe。

更宽松的语法

这次更新中放宽了两处语法的书写规范,不仅让新人更容易上手,也简化了代码的输入。

不再需要双下划线

在之前的版本中,如果我们不想定义某个变量的名字,我们必须使用双下划线来抛弃来定义变量名。现在,F# 中可以使用类似 C# 的单下划线来忽略变量的名字了。

for _ in 1..10 do ()

type C'() =
    member _.M() = ()

构造函数与静态方法的参数位置放宽

之前的 F# 编译器中,第一个参数的缩进决定了其余参数的缩进,这在编写代码的时候可能会让人感到恼火。

之前的参数缩进要求
type OffsideCheck(a:int,
                  b:int, c:int,
                  d:int) = class end

type C() =
    static member M(a:int,
                    b:int, c:int,
                    d:int) = 1

现在这个限制被放宽了。

type OffsideCheck'(a:int,
        b:int, c:int,
        d:int) = class end

type C'() =
    static member M(a:int,
      b:int, c:int,
      d:int) = 1

预览功能

就像 C# 编译器一样,F# 编译器现在支持通过配置开启预览语法了,目前的预览语法主要有两个:nameof 以及 open 静态类。

Nameof

nameof 函数算得上是社区呼声最高的功能之一了,它的作用就跟 C# 中的同名关键字一样,用来获取变量的名称。目前这个功能还不算 100% 的完善,所以仍然需要社区的使用反馈。

open System

let combineLengths (str1: string) (str2: string) =
    if isNull str1 then
        raise (ArgumentNullException(nameof str1))
    elif isNull str2 then
        raise (ArgumentNullException(nameof str2))
    else
        str1.Length + str2.Length

module M =
    let f x = x

printfn "%s namespace is opened right now" (nameof System)

printfn "Module %s has a function called %s" (nameof M) (nameof M.f)

open 静态类

在 C# 中,可以通过 using 一个静态类来暴露其中所有的静态方法,这样调用的时候就不必总是加上前缀了。一些 C# 编写的第三库也利用来这个特性来打造出了自己的 DSL,所以在 F# 中,这个特性的需求也非常大。目前 open 静态类的特性也只是作为一个预览功能放出,更完善的设计也需要接收更多社区中的使用者的意见。不过现在我们可以看看用了这个特性的代码长啥样。

open System.Math

let verifyMathWorks () =
    printfn "Just verifying that sin(pi) is indeed zero..."
    Sin(PI) = 0.0

[<AbstractClass; Sealed>]
type MyStaticClass =
    static member L(x: int) = x
    static member M(x: int) = x
    static member N(x: int) = x
    static member O(x: int) = x
    static member P(x: int) = x

open MyStaticClass

let x = 12
let xAgain = L(M(N(O(P(x)))))