F#如何通过等效接口
我知道当我看到这个答案时我会笑,但不知为什么我看不到它 由于某些原因,我无法在一个参数中传递多个func(因为缺少更好的词语) 例如,假设我有一个IDoSomething,它有3种方法:F#如何通过等效接口,f#,F#,我知道当我看到这个答案时我会笑,但不知为什么我看不到它 由于某些原因,我无法在一个参数中传递多个func(因为缺少更好的词语) 例如,假设我有一个IDoSomething,它有3种方法: 1.) DoIt() 2.) DoItMore() 3.) DoItMost() 在OO中,我会这样做: type MyController(something:IDoSomething) = let a = something.DoIt() let b = something.DoItMo
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个函数的模块。但我如何将其传递到我的控制器?我是否必须将每个函数作为单独的函数传递?我有点想通过整个模块,呵呵:-)使用记录类型:
type MyFunctions = {
DoIt: (unit -> unit);
DoItMore: (unit -> unit);
DoItMost: (unit -> unit);
}
那你可以
type MyController(functions: MyFunctions) =
let a = functions.DoIt()
let b = functions.DoItMore()
let c = functions.DoItMost()
您可以通过引用模块的函数来实例化记录类型:
module MyModule =
let doIt() =
Console.WriteLine("Do It!")
let doItMore() =
Console.WriteLine("Do It More!")
let doItMost() =
Console.WriteLine("Do It Most!")
let myRecord = {
DoIt = doIt;
DoItMore = doItMore;
DoItMost = doItMost;
}
let controller =
MyController(myRecord)
或者你可能想
type MyController(doIt: (unit -> unit), doItMore: (unit -> unit), doItMost: (unit -> unit))
// ... Etc
旁注:F#中大量的单元
通常表示设计不好
编辑:'s是这个问题的正确答案。这个问题似乎一次又一次地出现,不知何故,被接受的答案往往是“功能记录”。这样做没有合理的动机记录用于数据。它们具有结构上的相等性,而将函数放入其中会完全破坏结构上的相等性,因为函数没有结构上的相等性 接口 那么,F#中的接口的替代方案是什么 如果你一定要把函数组合在一起,F#可以让你定义。是:接口:
type IDoSomething =
abstract DoIt : unit -> unit
abstract DoItMore : unit -> unit
abstract DoItMost : unit -> unit
这个语言特性是存在的,所以如果你需要一个接口,没有理由为它想出一些奇怪的替代品
但是,它不起作用
对,它不是功能性的,但也不是创建功能记录
问题是是否有一种单一的、普遍存在的将相关函数分组在一起的函数方式。Haskell有类型类,Clojure有协议(对我来说,这看起来有点像类型类,但我几乎不是Clojure专家)
F#既没有类型类也没有协议;在语言中,最接近的是接口
功能
综上所述,函数式编程的基本构建块是:函数。有时,函数由其他函数组成,或者返回其他函数。我们称之为
惯用的函数代码通常通过高阶函数表示。不是将接口传递给函数,而是传递其他函数:
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
函数具有类型字符串列表->(奇偶校验*int列表)列表
。它有什么作用?它获取字符串列表,丢弃非整数的字符串,并按奇偶校验(偶数/奇数)对它们进行分组:
所以,它最终还是(有点)有用的
从OOP到FP
在这篇文章的开头,我写道:“如果你一定要把函数分组在一起。”。我写“如果”是有原因的。即使在OOD中,我们也知道,我们不应该强迫客户机依赖于它不需要的函数。将一组函数传递给客户机很容易违反这一原则。接口定义的成员越多,违反的风险就越大
除此之外,根据下文,“客户机[…]拥有抽象接口”(,第11章)。换句话说,客户机声明它需要什么,接口必须符合这一点;定义接口的不是实现
一旦您开始遵循这些以及其他内容,您应该开始意识到,您定义的接口越细粒度越好。如果客户机需要多个成员,则始终可以将两个接口作为两个参数传递,但如果接口已定义成员,则永远不能将其从接口中删除
简而言之,这就是可扩展性:可以扩展,但不能减少
在OOD中,接口在理想情况下应该只定义一个成员,但在函数式编程中,这种多态性有一个更自然的候选者:函数
因此,将函数作为参数传递。这是一种实用的方法
编辑:另请参见(免费单子)从技术上讲,传递多个函数至少有五种不同的方式:
module MyModule =
let doIt() =
Console.WriteLine("Do It!")
let doItMore() =
Console.WriteLine("Do It More!")
let doItMost() =
Console.WriteLine("Do It Most!")
let myRecord = {
DoIt = doIt;
DoItMore = doItMore;
DoItMost = doItMost;
}
let controller =
MyController(myRecord)
- 作为论据,咖喱
- 作为参数,元组
- 作为记录类型实例的字段值
- 作为具体或抽象类类型实例的字段值(val…)或属性(具体或抽象成员…)
- 作为接口类型实例的属性(抽象成员)
type DoSomethingInstruction<'a> =
| DoIt of 'a
| DoItMore of 'a
| DoItMost of 'a
let private mapI f = function
| DoIt next -> DoIt (next |> f)
| DoItMore next -> DoItMore (next |> f)
| DoItMost next -> DoItMost (next |> f)
type DoSomethingProgram<'a> =
| Free of DoSomethingInstruction<DoSomethingProgram<'a>>
| Pure of 'a
let rec bind f = function
| Free x -> x |> mapI (bind f) |> Free
| Pure x -> f x
let doIt = Free (DoIt (Pure ()))
let doItMore = Free (DoItMore (Pure ()))
let doItMost = Free (DoItMost (Pure ()))
type DoSomethingBuilder () =
member this.Bind (x, f) = bind f x
member this.Return x = Pure x
member this.ReturnFrom x = x
member this.Zero () = Pure ()
let doDomething = DoSomethingBuilder ()
let p = doDomething {
do! doIt
do! doItMore
do! doItMost }
let rec interpret = function
| Pure x -> x
| Free (DoIt next) -> printfn "Doing it."; next |> interpret
| Free (DoItMore next) -> printfn "Doing it more!"; next |> interpret
| Free (DoItMost next) -> printfn "DOING IT MOST!"; next |> interpret
> interpret p;;
Doing it.
Doing it more!
DOING IT MOST!
val it : unit = ()