Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F#如何通过等效接口_F# - Fatal编程技术网

F#如何通过等效接口

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

我知道当我看到这个答案时我会笑,但不知为什么我看不到它

由于某些原因,我无法在一个参数中传递多个func(因为缺少更好的词语)

例如,假设我有一个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个函数的模块。但我如何将其传递到我的控制器?我是否必须将每个函数作为单独的函数传递?我有点想通过整个模块,呵呵:-)

使用记录类型:

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…)或属性(具体或抽象成员…)
  • 作为接口类型实例的属性(抽象成员)
将多个函数耦合在一起是否有意义取决于您的具体问题领域,以及您对每种方法的某些优缺点的权衡程度。。。更不用说习俗、文化、风格和品味了。所谓的最佳实践从来都不是普遍有效的;这些只是暗示。实用主义是最佳实践

专家F#3.0如是说(第579页):“建议:使用对象接口类型,而不是元组或函数记录。 在第5章中,您看到了显式表示操作字典的各种方法,例如使用函数元组或函数记录。一般来说,我们建议您为此使用对象接口类型,因为与实现它们相关联的语法通常更方便。”

我在中写的大部分内容,我仍然认为F是正确的和惯用的。然而,后来我了解到
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 = ()