oop - 我应该编写利用 Intellisense 的代码吗?

标签 oop f# functional-programming intellisense purely-functional

我从 F# 开始,在理解语法方面取得了一些进展。但是,我仍然不清楚使用 F# 功能的最佳方式。在我来自的 Python 中,通常有一种“最好的”(几乎是规范的)做事方式。也许F#也是如此,但我还没有弄清楚。所以我下面的问题是关于使用 F# 的最佳方式,而不是关于 F# 语法的技术问题。

最近我看了 Eric Meijer 博士的视频 (C9 Lectures - Functional Programming Fundamentals Chapter 2 of 13) Meijer 博士在其中赞扬了 OOP 的点表示法,观察到它允许 Intellisense 显示可用方法的列表。他感叹这样的功能在纯 FP 中是不可用的,这通过帮助程序员“前进”而使编程变得如此容易。

一些实验表明,Intellisense 当然适用于 F# 类,但也适用于 F# 记录,与类一样,它使用点表示法。这意味着一个人可以塑造自己的代码,以便利用 Intellisense 而无需一路编写类(我假设在 F# 中类比记录更重且更慢,如果我错了,请纠正我)。

以下代码显示了两种编写执行相同操作的代码(称为“版本”)的方法:

// Create a record type with two values that are functions of two arguments
type AddSub = {add2: int -> int -> int; sub2: int -> int -> int}

// Instantiate a record
let addsub a =
    {add2 = (fun x y -> a + x + y); sub2 = (fun x y -> a - x - y)}

// Returns 7, Intellisense works on (addsub 0).
(addsub 0).add2 3 4
// Returns 3, Intellisense works on (addsub 10).  
(addsub 10).sub2 3 4

// Create two functions of three arguments
let add3 a x y = a + x + y
let sub3 a x y = a - x - y

// Also got 7, no Intellisense facility here
add3 0 3 4
// Also got 3, no Intellisense facility here
sub3 10 3 4

这表明在纯 FP 和 OOP 之间存在一种中间策略:使用函数值创建记录类型,如上。这种策略将我的代码组织在以对象(记录实例)为中心的有意义的单元中,并允许我使用 Intellisense,但缺少类提供的一些特性,如继承和子类多态性(如果我在这里错了,请再次纠正我)。

来自 OOP 背景,我觉得如果像 a 这样的对象在上面的代码中,在某种程度上比参数 x 和 y 更“重要”(我将保留该术语未定义),这样的编码策略是合理的,无论是基于代码组织还是使用 Intellisense 的能力。另一方面,由于 OOP 的复杂性,我宁愿留在“纯粹的”FP 领域。

记录的使用是否是两种极端选择(OOP 和纯 FP)之间值得折衷的选择?

一般来说,给定三种替代方案(纯 FP、上述记录或类),在哪些情况下一种替代方案优于其他方案的一般准则是什么?

最后,是否有其他可用的编码策略可以帮助我组织代码和/或利用 Intellisense?

最佳答案

Intellisense 在 F# 中仍然可以正常工作,但在模块级别而不是在类级别。即,我刚刚输入 List.一旦我输入了这个点,VS Code(使用提供 F# Intellisense 的 Ionide 插件)给了我一个可能的完成列表:append , average , averageBy , choose , chunkBySize ...

要从您自己的函数中获得好处,请将它们放在一个模块中:

module AddSub =
    let add2 x y = x + y
    let sub2 x y = x - y
    let add3 a x y = a + x + y
    let sub3 a x y = a - x - y

现在当你输入 AddSub. , 输入点后,Intellisense 会提示 add2 , add3 , sub2 , 和 sub3尽可能跟进。然而,您已经以“适当的”F# 风格保持了函数“干净”和可 curry 。

最后,关于功能设计的另一条建议。您提到有一个参数(如 aadd3 函数中的 sub3 )在某种程度上比其他参数更“重要”的函数。在 F# 中,任何此类参数都应该是最后一个参数,因为这允许您使用 |> 将其放入函数链中。运算符,如下所示:
let a = 20
a |> AddSub.add3 5 10 |> AddSub.sub3 2 3  // Result: 30

或者更确切地说,当存在从单个起始值开始的操作“管道”时,使用大多数人喜欢的样式:
let a = 20
a
|> AddSub.add3 5 10
|> AddSub.sub3 2 3
// Result: 30

当管道中有更多操作时,垂直排列管道变得更加重要。我的经验法则是,如果在管道中指定了两个以上的“额外”参数(上面的管道有四个“额外”参数,add3sub3 函数各有两个),或者如果任何一个“额外”参数比单个值更复杂(例如,如果一个参数是匿名函数,如 (fun x -> sprintf "The value of x was %d" x) 或类似的),那么您应该垂直排列它。

附言如果您还没有阅读,请阅读 Scott Wlaschin 的优秀系列文章 Thinking Functionally .这将有助于解释有关此答案的许多事情,例如为什么我建议将“最重要”的论点放在最后。如果您没有立即理解我关于如何将它与 |> 一起使用的简短评论。参数,或者如果您对此答案有任何其他困惑,那么您可能会从 Scott 的文章中受益匪浅。

关于oop - 我应该编写利用 Intellisense 的代码吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42469093/

相关文章:

java - 为什么这个方法不使用对象的任何属性?

oop - 术语 WRAPPER 在编程中的使用位置和方式,它有什么帮助?

f# - F# 中是否存在与 DU 的 [<RequireQualifiedAccess>] 属性等效的 OCaml?

在 Scala 中调试功能代码

scala - 在Scala中应用几个字符串转换

performance - Dart功能样式的性能提升

java:instance关键字的用法

c++ - 该类有多少个构造函数?

f# - 列表中的扩展方法?

f# - 基于无限序列的 Observable 动画图表