简体   繁体   English

通用鸭子在F#打字?

[英]generic duck typing in F#?

using let inline and member constraints I'll be able to make duck typing for known members but what if I would like to define a generic function like so: 使用let inline和member约束我将能够为已知成员进行duck typing,但是如果我想定义一个泛型函数如下:

let duckwrapper<'a> duck = ... 让duckwrapper <'a> duck = ...

with the signature 'b -> 'a and where the returned value would be an object that implemented 'a (which would be an interface) and forwarded the calls to duck. 使用签名'b - >'a并且返回值将是实现'a(将是一个接口)并将调用转发给duck的对象。

I've done this in C# using Reflection.Emit but I'm wondering if F# reflection, quotations or other constructs would make it easier. 我在C#中使用Reflection.Emit完成了这个,但我想知道F#反射,引用或其他结构是否会使它变得更容易。

Any suggestions on how to accomplish this? 有关如何完成此任务的任何建议?

EDIT after reading Tims answer I thought I'd give a bit more details 在阅读Tims回答之后编辑 ,我想我会提供更多细节

What I was thinking of when I wrote about using quotations to help was something like: 当我写关于使用引用来帮助时,我想到的是:

{new IInterface with member x.SayHello() = !!<@ %expr @>}

!! being an operator translating the quotation to a function and %expr being the unit of work for the method. 是将运算符转换为函数的运算符,%expr是该方法的工作单元。 I'd be able to translate the expression to a function (I guess) but wouldn't know how to 我能够将表达式转换为函数(我猜),但不知道如何

of course this wouldn't do the trick completely either since IInterface would be 'a which is where I hope F# reflection might have some handy functions so that I could construct a type based on a type object and some function values 当然这不会完全成功,因为IInterface将是'我希望F#反射可能有一些方便的功能,以便我可以构建一个基于类型对象和一些函数值的类型

EDIT As an update to Tomas Petricek answer I'll give some code to explain my needs 编辑作为Tomas Petricek的更新答案,我将提供一些代码来解释我的需求

type SourceRole =
   abstract transfer : decimal -> context

and context(sourceAccount:account, destinationAccount) =
   let source = sourceAccount
   let destination = destinationAccount

   member self.transfer amount = 
     let sourcePlayer = 
       {new SourceRole with
          member this.transfer amount =
              use scope =  new TransactionScope()
              let source = source.decreaseBalance amount
              let destination = destination.increaseBalance amount
              scope.Complete()
              context(source,destination)
              }
     sourcePlayer.transfer(amount)

which is a try at porting "the" textbook example of DCI in F#. 这是尝试在F#中移植DCI的“教科书”示例。 The source and destination are DCI roles. 源和目标是DCI角色。 It's the idea that any data object that adhere's to a specific contract can play those. 这是一个想法,任何遵守特定合同的数据对象都可以发挥作用。 In this case the contract is simple. 在这种情况下,合同很简单。 source needs a memberfunction called decreaseBalance and destination needs a member function called increaseBalance. source需要一个名为decreaseBalance的成员函数,而destination需要一个名为increaseBalance的成员函数。 I can accomplish that for this specific case with let inline and member constraints. 我可以通过内联和成员约束来实现这个特定情况。 But I'd like to write a set of functions that given an interface and an object. 但是我想编写一组给出接口和对象的函数。 In this case it could be source (as the object) and 在这种情况下,它可以是源(作为对象)和

type sourceContract = 
   abstract decreaseBalance : decimal -> sourceContract

as the type. 作为类型。 The result would be an object of type sourceContract that would pipe method calls to a method with the same name on the source object. 结果将是sourceContract类型的对象,它将方法调用传递给源对象上具有相同名称的方法。

F# reflection ( Microsoft.FSharp.Reflection ) is an F#-friendly wrapper around the plain System.Reflection APIs, so I don't think it would add anything here. F#reflection( Microsoft.FSharp.Reflection )是一个围绕普通System.Reflection API的F#友好包装器,所以我认为它不会在这里添加任何内容。

Quotations can't define new types: (you'd need to define a new type to do your interface-based duck typing) 引号无法定义新类型:(您需要定义一个新类型来执行基于接口的鸭子类型)

> <@ { new IInterface with member x.SayHello = "hello" } @>;;

  <@ { new IInterface with member x.SayHello = "hello" } @>;;
  ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stdin(7,4): error FS0449: Quotations cannot contain object expressions
> <@ type Test() = class end @>;;

  <@ type Test() = class end @>;;
  ---^^^^

stdin(8,4): error FS0010: Unexpected keyword 'type' in quotation literal

Reflection.Emit is still the way to go with this. Reflection.Emit仍然是这样的方式。

Edit: 编辑:

I hope F# reflection might have some handy functions so that I could construct a type based on a type object and some function values 我希望F#reflection可能有一些方便的函数,这样我就可以构建一个基于类型对象和一些函数值的类型

I'm afraid it doesn't. 我担心不会。 Here's the documentation on F# reflection: http://msdn.microsoft.com/en-gb/library/ee353491.aspx 这是关于F#反射的文档: http//msdn.microsoft.com/en-gb/library/ee353491.aspx

You can compile F# quotations using components from F# PowerPack . 您可以使用F#PowerPack中的组件编译F#引用。 So I think you could use quotations to generate and execute code at runtime. 所以我认为你可以使用引号在运行时生成和执行代码。 If you write a quotation representing a function & compile it you'll get a function value that you could use to implement an interface. 如果你编写一个代表函数的引用并编译它,你将获得一个可用于实现接口的函数值。 Here is a trivial example: 这是一个简单的例子:

#r "FSharp.PowerPack.Linq.dll"
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.QuotationEvaluation

// Create a part using "Expr." calls explicitly
let expr = Expr.Value(13)
// Create a part using quotation syntax 
let expr2 = <@ (fun x -> x * %%expr) @>

// Compile & Run
let f = expr2.Compile()()
f 10

You can mix quotation syntax and calls to Expr , which makes it easier to compose code from basic blocks. 您可以混合使用引用语法和对Expr调用,这样可以更轻松地从基本块组合代码。 The compilation is a bit stupid (currently) so the generated code won't be as efficient as usual F# code (but you'll need to measure it in your case). 编译有点愚蠢(目前),因此生成的代码将不如通常的F#代码那样高效(但是你需要在你的情况下测量它)。

I'm not quite sure I understand what exactly are you trying to do, so if you can provide more details, I can give more specific answer. 我不太清楚我明白你究竟想做什么,所以如果你能提供更多细节,我可以给出更具体的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM