F#如何传递等效的接口 [英] F# how to pass equivalent of interface

查看:82
本文介绍了F#如何传递等效的接口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I know I will laugh when I see this answer, but for some reason I don't see it.

由于某种原因,这使我难以理解如何在一个参数中传递多个func(因为缺少更好的单词.)

For some reason it is eluding me how to pass multiple func in one parameter (for lack of better words.)

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

For instance, lets say I have IDoSomething that has 3 methods:

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

在OO中,我会这样做:

in OO, I would do this:

type MyController(something:IDoSomething) =
   let a = something.DoIt()
   let b = something.DoItMore()
   let c = something.DoItMost()

因此,对于F#,我将拥有一个具有上述3个功能的模块.但是我如何将其传递给我的控制器?我是否必须将每个作为单独的函数传递?我有点想传递整个模块,呵呵:-)

So, for F# I would have a module with the 3 functions mentioned above. But how would I pass that into my controller? Would I have to pass each as a separate function instead? I kinda feel like I want to pass the whole module hehe :-)

推荐答案

这个问题似乎一遍又一遍地出现,并且以某种方式公认的答案常常被证明是函数的记录".这样做没有合理的动机. 记录用于数据.它们具有结构上的相等性,因为函数具有结构上的相等性,所以通过将函数放入函数中会完全破坏它们.

This question seems to come up again and again, and somehow the accepted answer often turns out to be 'a record of functions'. There's no reasonable motivation for doing this. Records are for data. They have structural equality, which is totally destroyed by putting functions into them, since functions don't have structural equality.

那么,F#中接口的替代方法是什么?

So, what's the alternative to interfaces in F#?

好吧,如果您完全将必须组功能结合在一起,则F#使您可以定义

Well, if you absolutely must group functions together, F# enables you to define interfaces. Yes: interfaces:

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

该语言功能已经存在,因此,如果您需要一个界面,则没有理由提出一些奇怪的替代方法.

This language feature exists, so if you need an interface, there's no reason to come up with some weird replacement for it.

对,它不是功能性的,但也没有创建功能记录.

Right, it isn't Functional, but neither is creating a record of functions.

问题是,是否存在一种将相关功能组合在一起的泛在功能的单一方法. Haskell具有类型类,而Clojure具有协议(在我看来,它有点像类型类,但是我并不是Clojure专家).

The question is whether there's a single, ubiquitous Functional way of grouping related functions together. Haskell has type classes and Clojure has protocols (which, to me, look a bit like type classes, but then, I'm hardly a Clojure expert).

F#既没有类型类也没有协议.同样,您所使用的语言中最接近的是界面.

F# has neither type classes nor protocols; the closest you get in the language is, again, interfaces.

总而言之,函数式编程的基本组成部分是:函数.有时,功能是由其他功能组成的,或者返回其他功能.我们称这些高阶函数.

All that said, the fundamental building block of Functional Programming is: functions. Sometimes, functions are composed from other functions, or return other functions. We call these higher-order functions.

惯用功能代码通常通过高阶函数表示. 传递其他功能:

Idiomatic Functional code is often expressed through higher-order functions. Instead of passing in an interface to a function, pass other functions:

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).我不知道它的作用(我刚刚做了),但要点是foobarbaz功能.例如,foo是类型'a -> 'b的函数.

Because of type inference, even such a nonsense example as above compiles. It has the type ('a -> 'b) -> ('b list -> 'c list) -> ('c -> 'd) -> ('a list -> ('d * 'c list) list). I have no idea what it does (I just made it up), but the point is that foo, bar, and baz are functions. As an example, foo is a function of the type 'a -> 'b.

即使具有run这样可笑的功能,也可以应用它,它实际上可能是有道理的:

Even with such a ridiculous function as run, you can apply it, and it may actual make sense:

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.它有什么作用?它需要一个字符串列表,丢弃不是整数的字符串,并按奇偶校验(偶数/奇数)对它们进行分组:

The runP function has the type string list -> (Parity * int list) list. What does it do? It takes a list of strings, discards those that aren't integers, and groups them by parity (even/odd):

> runP ["Foo"; "1"; "42"; "Bar"; "Baz"; "1337"];;
val it : (Parity * int list) list = [(Odd, [1; 1337]); (Even, [42])]

所以,事实证明它毕竟是有用的!

So, it turned out to be (sort of) useful after all!

在此咆哮的开头,我写道:如果您绝对将必须分组在一起工作,".我写 if 是有原因的.即使在OOD中,从接口隔离原则,我们知道我们不应该强迫客户取决于它不需要的功能.将一组功能传递给客户很容易违反该原则.接口定义的成员越多,违反的风险就越大.

In the beginning of this rant, I wrote: "if you absolutely must group functions together". There's a reason I wrote if. Even in OOD, from the Interface Segregation Principle, we know that we shouldn't force a client to depend on functions it doesn't need. Passing a group of functions to a client can easily violate that principle. The more members an interface defines, the bigger the risk of violation.

除此之外,根据依赖关系倒置原则,客户[... ]拥有抽象接口"( APPP ,第11章).换句话说,客户端陈述了它的需求,并且接口必须符合该需求;定义接口的不是实现.

In addition to that, from the Dependency Inversion Principle follows that "clients [...] own the abstract interfaces" (APPP, chapter 11). In other words, the client states what it needs, and the interface must conform to that; it's not the implementation that defines the interface.

一旦您遵循这些规则,其余的 SOLID原则 ,您应该开始意识到,定义的界面越精细越好. 逻辑结论是仅使用单一方法.如果客户端需要一个以上的成员,则始终可以将两个接口作为两个参数传递,但是如果已经定义了一个成员,则永远不能从该接口中删除该成员.

Once you start following these, and the rest of the SOLID principles, you should begin to realise that the more granular you define your interfaces, the better. The logical conclusion is to define all interfaces with only a single method. If a client needs more than one member, you can always pass two interfaces as two arguments, but you can never remove a member from an interface if it's already defined.

简而言之就是可扩展性:您可以扩展,但不能减小.

That's extensibility in a nutshell: you can extend, but you can't diminish.

在OOD中,理想情况下,接口应该只定义一个成员,但是在函数式编程中,对于这种多态性,我们有一个更自然的候选者:函数.

In OOD, interfaces should ideally define only a single member, but in Functional Programming, we have a more natural candidate for such polymorphism: a function.

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

Thus, pass functions as arguments. It's the Functional way to do it.

编辑:另请参见我的其他答案(适用于免费monad)

See also my other answer (on free monads)

这篇关于F#如何传递等效的接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆