gpt4 book ai didi

F#如何传递等效的接口(interface)

转载 作者:行者123 更新时间:2023-12-03 11:43:51 24 4
gpt4 key购买 nike

我知道当我看到这个答案时我会笑,但由于某种原因我没有看到它。

出于某种原因,我无法在一个参数中传递多个函数(因为没有更好的词。)

例如,假设我有 IDoSomething 有 3 种方法:

1.)  DoIt()
2.) DoItMore()
3.) DoItMost()

在 OO 中,我会这样做:
type MyController(something:IDoSomething) =
let a = something.DoIt()
let b = something.DoItMore()
let c = something.DoItMost()

因此,对于 F#,我将拥有一个具有上述 3 个功能的模块。但是我如何将它传递给我的 Controller ?我是否必须将每个作为单独的函数传递?我有点想通过整个模块嘿嘿:-)

最佳答案

这个问题似乎一次又一次地出现,不知何故,公认的答案往往是“功能记录”。这样做没有合理的动机。 记录用于数据 .它们具有结构上的平等性,而将功能放入其中会完全破坏它们,因为功能不具有结构上的平等性。

接口(interface)

那么,F# 中接口(interface)的替代方案是什么?

好吧,如果您绝对必须将函数组合在一起,F# 允许您定义 interfaces .是:接口(interface):

type IDoSomething =
abstract DoIt : unit -> unit
abstract DoItMore : unit -> unit
abstract DoItMost : unit -> unit

这个语言特性是存在的,所以如果你需要一个界面,没有理由想出一些奇怪的替代品。

但是,它不是功能性的

是的,它不是函数式的,但也不是创建函数记录。

问题是是否存在将相关功能组合在一起的单一、普遍的功能方式。 Haskell 有 类型类 Clojure 有 协议(protocol) (对我来说,它看起来有点像类型类,但是,我几乎不是 Clojure 专家)。

F# 既没有类型类也没有协议(protocol);再次,您在该语言中获得的最接近的是接口(interface)。

职能

综上所述,函数式编程的基本构建 block 是: 功能 .有时,函数由其他函数组成,或者返回其他函数。我们称这些为 higher-order functions .

惯用的函数式代码通常通过高阶函数表示。 不是将接口(interface)传递给函数,而是传递其他函数 :
let run foo bar baz = List.map foo >> bar >> List.groupBy baz

由于类型推断,即使是上面这样的废话示例也可以编译。它的类型为 ('a -> 'b) -> ('b list -> 'c list) -> ('c -> 'd) -> ('a list -> ('d * 'c list) list) .我不知道它做了什么(我只是编造的),但关键是 foo , bar , 和 baz是函数。例如, foo'a -> 'b 类型的函数.

即使有像 run 这样荒谬的功能,您可以应用它,它可能实际上是有意义的:
type Parity = Even | Odd
let parity i =
match i % 2 with
| 0 -> Even
| _ -> Odd

open System

let tryParse s =
match Int32.TryParse s with
| true, i -> Some i
| _ -> None
let runP = run tryParse (List.choose id) parity
runP函数的类型为 string list -> (Parity * int list) list .它有什么作用?它需要一个字符串列表,丢弃那些不是整数的字符串,并按奇偶校验(偶数/奇数)对它们进行分组:
> runP ["Foo"; "1"; "42"; "Bar"; "Baz"; "1337"];;
val it : (Parity * int list) list = [(Odd, [1; 1337]); (Even, [42])]

所以,它毕竟是(某种)有用的!

从 OOP 到 FP

在这篇咆哮的开头,我写道:“如果你绝对必须将函数组合在一起”。我写 if 是有原因的。即使在 OOD 中,来自 Interface Segregation Principle ,我们知道我们不应该强制客户依赖它不需要的功能。将一组函数传递给客户端很容易违反该原则。接口(interface)定义的成员越多,违规的风险就越大。

除此之外,来自 Dependency Inversion Principle遵循“客户 [...] 拥有抽象接口(interface)”( APPP,第 11 章)。换句话说,客户端声明它需要什么,接口(interface)必须符合它;定义接口(interface)的不是实现。

一旦你开始关注这些,以及 SOLID principles 的其余部分,你应该开始意识到你定义的接口(interface)越细越好。 The logical conclusion is to define all interfaces with only a single method .如果客户端需要多个成员,则始终可以将两个接口(interface)作为两个参数传递,但如果接口(interface)已定义,则永远不能从接口(interface)中删除成员。

简而言之,这就是可扩展性:您可以扩展,但不能减少。

在 OOD 中,理想情况下,接口(interface)应该只定义一个成员,但在函数式编程中,我们有一个更自然的多态候选者:函数。

因此,将函数作为参数传递。这是功能性的方法。

编辑:另见 my other answer (关于自由单子(monad))

关于F#如何传递等效的接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34011895/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com