传递解析为多种类型的泛型函数的最佳方法是什么

Eug*_*hev 7 f# functional-programming

对于背景:它是功能DI的变体.在Scott的帖子后,我写了一个翻译.扭曲的是我的翻译是通用的,并根据你提供给它的东西进行参数化.

出于测试目的,我想通过另一个口译员,其中就是抄袭 - 我怎么办?以下是问题的简化概述:

let y f =
    let a = f 1
    let b = f 2L
    (a,b)
Run Code Online (Sandbox Code Playgroud)

f是我的通用解释器,但在这里它显然受到第一次使用的约束int -> 'a.在这个简化的场景中,我可以只传递两次解释器,但在我的实际实现中,类型空间相当大(基类型x3输出类型).

是否有一些F#机制可以让我这样做,没有太多的开销?

Fyo*_*kin 10

您不能在F#中使用函数执行此操作.函数作为值传递时会失去通用性.

但是,F#确实有一种机制可以做到这一点,虽然有点笨拙:接口.接口方法可以是通用的,因此您可以使用它们来包装您的泛型函数:

type Wrapper =
    abstract member f<'a> : 'a -> 'a

let y (w: Wrapper) = 
   let a = w.f 1 
   let b = w.f 2L 
   (a, b)

let genericFn x = x

// Calling y:
y { new Wrapper with member __.f x = genericFn x }
Run Code Online (Sandbox Code Playgroud)

缺点是,你不能回到高阶函数,以免失去通用性.你必须有接口一直到海龟.例如,您无法通过将其抽象为函数来简化实例创建:

let mkWrapper f = 
   // no can do: `f` will be constrained to a non-generic type at this point
   { new Wrapper with member __.f x = f x }
Run Code Online (Sandbox Code Playgroud)

但是你可以在另一边提供一些便利.至少摆脱类型注释:

type Wrapper = abstract member f<'a> (x: 'a): 'a

let callF (w: Wrapper) x = w.f x

let y w = 
   let a = callF w 1 
   let b = callF w 2L 
   (a,b)
Run Code Online (Sandbox Code Playgroud)

(注意:上面的代码中可能存在轻微的语法错误,因为我正在手机上写字)


Gus*_*Gus 5

不确定您是否仍然感兴趣,因为您已经接受了答案,但是正如@Fyodorsoikin 所要求的那样,这是“静态”方式,这一切都发生在编译时,因此没有运行时开销:

let inline y f =
    let a = f $ 1
    let b = f $ 2L
    (a, b)

type Double = Double with static member inline ($) (Double, x) = x + x
type Triple = Triple with static member inline ($) (Triple, x) = x + x + x

type ToList = ToList with static member        ($) (ToList, x) = [x]

let res1 = y Double
let res2 = y Triple
let res3 = y ToList
Run Code Online (Sandbox Code Playgroud)

当我需要在任意结构上使用泛型函数时,我会使用这种技术,我使用单个方法“Invokable”来命名类型。

更新

要向函数添加参数,请将其添加到 DU,如下所示:

type Print<'a> = Print of 'a with
    static member inline ($) (Print printer, x) = printer (string x)

let stdout (x:string) = System.Console.WriteLine x
let stderr (x:string) = System.Console.Error.WriteLine x

let res4 = y (Print stdout)
let res5 = y (Print stderr)
Run Code Online (Sandbox Code Playgroud)

这只是一个快速简单的示例代码,但这种方法可以改进:您可以使用方法名称而不是运算符,您可以避免在声明中重复 DU,并且您可以组合 Invokables。如果您对这些增强功能感兴趣,请告诉我。我之前在生产代码中使用了这种方法的改进,但从未遇到任何问题。

  • 我也用它们。广泛。用法本身不是作弊。作弊是在这个特定问题的上下文中使用它。 (3认同)