F#奇妙游(13):代码风格

F# 代码风格

这玩意有两点不能忘记,第一点这是很高级的内容,不要相信那些要求你一开始学习F#就要这样就要那样;第二点,这些内容根据不同的项目和不同的组织时时长变动的,并没有什么一定要遵循的准则。基于这两点认知(这两点是认知吗?是的),这是高级要求,这是项目实施的特定要求,该怎么做呢?

  1. 知道有这个事情,这个还挺重要,有些时候,你必须知道有那个事,为什么有那个事,那是个什么事,这是见识和阅历的问题;
  2. 只有在必要的时候,才引入这种约定,在此之前,选一个让自己舒服的实践,并根据自己的需要调整;
  3. 尽量尊重项目组、社区的规范,但是以不损害社区、项目利益为界限,比如,不能把一个很天才、能解决问题的但是不愿意遵守规范的人干掉,给他提供帮助(实用性)工具时最好的实践。

以下内容在上面的前提下讨论,主要是MSDN中F# style guide的学习笔记和个人认识。

好的F#代码的原则

以下是ChatGPT的扯淡:

1. 代码应该是简单的,不要过度设计,不要过度工程化,不要过度抽象化,不要过度泛化,不要过度模块化,不要过度面向对象化,不要过度函数式化,不要过度……
2. 代码应该是可读的,不要过度简化,不要过度压缩,不要过度精简,不要过度……
3. 代码应该是可维护的,不要过度复杂,不要过度……
4. 代码应该是可测试的,不要过度……
5. 代码应该是可扩展的,不要过度……
6. 代码应该是可重用的,不要过度……
7. 代码应该是可移植的,不要过度……
8. 代码应该是可靠的,不要过度……
9. 代码应该是高效的,不要过度……
10. 代码应该是安全的,不要过度……

以下才是正经的:

好的F#代码应该是简洁的、表达丰富的、可组合的。

F sharp提供了很多语言特性。使得我们能够用更少的代码,实现更丰富的功能。并且很好的运用泛型,来重用一些功能。 F sharp的内裤里面也包含了大量的非常有用的类型和函数,特别是用于处理那些数据集合的通用数据结构。所以对于库函数,特别是f12的核心库函数,以及其他的一些larger的就那库的使用,是我们在f sharp编程中间的一个很重要的原则。

这里其实说了有几层腻子。第1层意思就是,f sharp的语言本身提供的可组合表达能力强的语言特征。第2层意思。就是f沙浦的核心库提供的数据结构和函数。第3层意思。就是基于前面这两个。特征和f沙子本身的可组合的特性。他来使用一个net的库也是非常的容易。

好的F#代码应该有很强的交互特性

可交互性主要描述的是。 F sharp去使用其他语言的功能。或者是其他语言的使用艾莎的功能。所以这个不同语言中间的这个界面,就是只管中间有多少。所以在你编写f sharp代码的时候,你应该一直去思考。是怎么样调动你编写的函数?这里呢,在简爱的平台上呢,还有一个component设计准则来解释这个点net的多个语言之间的交互。

好的F#代码使用对象模型,但是不是面向对象的

在Excel中使用点内的平台的各种对象是非常容易的,并且非常完整的支持,包括内界面。私有和公开抽象类。但是怎么说呢,既然是使用Excel,那么同样的人就不能。就应该避免过度的面向对象的设计。这个跟第1点非常接近,就是说我们要用一些可以组合的函数实现我们编程的功能。

好的F#代码应该充分运用不可变特性

现在变成语言里面要实现比较高的性能,同城就要更好的利用内存的操作。变化是如何避免?变化的量是无可避免的。但是f上实现一个功能的时候,应该尽可能的去通过函数进行参与可变量的编程过程,去提供一些传递无变量的函数界面。

好的F#代码应该致力于更好地支持(自动化)工具应用

这里是一个很有意思的话题。比如说过度的使用无值的运算类型,就是在编程的过程中一个函数尽量避免保存中间结果,如果这样做的非常的过分的话,那么在调试这个函数的时候就是非常的麻烦。这个其实在函数式编程里面是一个很常见的现象,因为我们通过管道和链式调用,基本上没有存在这个中间变量的这个地方。那么这里很明显的,就是有一个设计的平衡点。

啊,另外一个就是注释里面的xml语法问题。如果注释写的比较合乎规范的话,那就爱奇艺里面能够得到我。更好的工具支持。

F#代码风格

考虑到现在代码的字格式化的都是用自动工具完成的,所以这里面。没有什么很复杂的需要讨论的。只是可以举几个啊,例子和一些比较小的原则性的问题啊。

let aVeryLongName = 
    aLongExpresion
    |> func1
    |> func2

在使用缩进的时候,应该像上面这个函数定义的一样,避免缩进受到变量和函数的长度。

其实在实际的使用中呢,就是早点换,早点换行。

let aVeryLongName = 
    match 
        aVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongExpresion
            parameter1
            parameter2
            parameter3
        with
    | Some _ -> ()
    | _ -> ()

代码格式化的这个例子呢,我看也没多大意思,因为。 ID也会自动处理吧。没有必要太去纠结这件事情。

F#编程约定

组织代码

F sharp的组织代码的时候,有两个基本的组件。第1个是命名空间namespace.,第2个是模块module。

如何使用命名空间和模块这样的概念来组织你的代码,取决于你的代码,是要被谁调用?我命名空间呢,在多类的平台上,会被别人译成命名空间。模块相应的会被编译成静态类。匿名空间呢,始终是。最顶层的。并且只能是最顶层的。而模块呢,还可以嵌套使用。命名空间可以在不同的文件之间共享,也就是说同一个命名空间,里面的内容可以在不同的文件里面进行增加,而模块就只能出现在一个文件里。另外模块的这个暴露我还可以用过,用不同的那个修饰方法。 Public private也就是是否是open。

根据上面的这个理解呢。我们在使用命名空间和模块的时候呢,就有以下一些准则。在最顶层的代码组织里面的推荐使用命名空间。如果在代码的最底层,说白了也就是在一个文件的这开头,如果是使用模块,那么在c sharp或者其他的语言里面调用的时候。就会发现需要增加一个静态的内才能调入模块里面的类型和函数。其实是否公开一个模块里面的名称呢?很多时候还是取决于这个模块是会被谁来使用?

在F sharp里面还有一个问题,需要纳入考虑就是open使用的顺序问题。后面调用的open呢,会覆盖前面调用的open。所以在调用open的时候呢,应该把你所有的open呢来做一个排序。

此外呢,在f夏普编程的时候呢,如果一个属性。额外的效应或者是作为一个状态,那么我们倾向于不把它放在一个模块里,而是把它放在一个类里面,因为模块编译到朵拉的平台上面,去了之后呢,它是一个静态的累,按照道理的只有静态的属性。

这个里面呢,一方面涉及到大型项目的组织问题,另外一个还涉及到县城安全的问题。

错误和异常处理

在f sharp呢,因为有。因为有可区分联合这样的语言工具,所以在处理异常或者是错误的时候呢,还是有一些比较简洁的晚安。

其实在函数式编程语言里面,一般是要尽量规避异常的概念,异常破坏了函数的纯洁性。但是f sharp竟然是点那个平台上的编程里面那么。尽量的使用,本来各平台的异常处理呢,也有可能比费劲的去实现一个异常处理的语义的一个,自己要更好一些。

偏应用和无值运算

这里有一种观点,就是应该尽量避免将偏应用或物质运算,暴露在f sharp提供给短的平台,其他语言的接口中。这个观点是有一定的意义的,因为比如说用c sharp编辑的程序员,可能对于函数式编程的这种概念理解起来有非常大的困难,所以说暴露给国内的平台的接口的,尽量包括国内的平台的。设计理念。但是在f sharp编程的内部,广泛的使用可组合的函数和无值运算的这种编程模式呢?非常有利的。

文档中给出了下面的这样编程的代码的事例。

let func name age =
    printfn $"My name is {name} and I am %d{age} years old!"

let funcWithApplication =
    printfn "My name is %s and I am %d years old!"

在这个例子中间的两个函数呢,第1个函数呢,我们可以很清楚的看到它的参数的个数参数的类型。而第2个函数呢。 ID就很难支持我们对他的。函数的类型进行推导,那我们首先来看一下rider是不是能够正确的识别这个参数的标识?

那么大还是肯定的,晚一点作为一个比较现代化的变音器呢,它很轻松的就识别了第2个函数的签名。但是呢,还有一个问题就是嗯。在参函数的签名中间,它的输入参数的命名本身也写到了一些信息,比如说第1个函数里面name和age。他们的名称本身就标注了这个函数,应该代表的意义。而第2个函数呢。隐含命名的,那么我们就不能很明确的一眼就看出它的参数到底代表是什么意义。

F# COM设计规范

XML文档注释

F#代码中以三个斜杠开头的注释,会被编译器识别为XML文档注释。这些注释可以用于生成文档,也可以用于IDE的智能提示。

/// <summary>A class represening a person.</summary>
type Person(name:string, age:int) =
    /// <summary>The person's name.</summary>
    member this.Name = name
    /// <summary>The person's age.</summary>
    member this.Age = age

命名空间、模块

使用命名空间(映射到.NET命名空间)和模块(映射到.NET静态类)来组织代码。命名空间可以跨文件,模块只能在一个文件中。

类型

对于那些mutable的类型,使用类,对于那些immutable的类型,使用record。

还可以使用interface来把相关的操作组织在一起。

名称

F#中还可以使用[]来指定编译后的名称。这样在F#中使用的名称和在C#中使用的名称就可以不同了。

[<CompiledName("MyNETClass")>]
type MyClass() =
    member this.MyMethod() = ()

总结

这篇是最没有任何价值的,我都不知道为啥要写。

posted @ 2023-07-15 18:04  大福是小强  阅读(7)  评论(0编辑  收藏  举报  来源