F#奇妙游(13):代码风格
F# 代码风格
这玩意有两点不能忘记,第一点这是很高级的内容,不要相信那些要求你一开始学习F#就要这样就要那样;第二点,这些内容根据不同的项目和不同的组织时时长变动的,并没有什么一定要遵循的准则。基于这两点认知(这两点是认知吗?是的),这是高级要求,这是项目实施的特定要求,该怎么做呢?
- 知道有这个事情,这个还挺重要,有些时候,你必须知道有那个事,为什么有那个事,那是个什么事,这是见识和阅历的问题;
- 只有在必要的时候,才引入这种约定,在此之前,选一个让自己舒服的实践,并根据自己的需要调整;
- 尽量尊重项目组、社区的规范,但是以不损害社区、项目利益为界限,比如,不能把一个很天才、能解决问题的但是不愿意遵守规范的人干掉,给他提供帮助(实用性)工具时最好的实践。
以下内容在上面的前提下讨论,主要是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() = ()
总结
这篇是最没有任何价值的,我都不知道为啥要写。