簡體   English   中英

通用鴨子在F#打字?

[英]generic duck typing in F#?

使用let inline和member約束我將能夠為已知成員進行duck typing,但是如果我想定義一個泛型函數如下:

讓duckwrapper <'a> duck = ...

使用簽名'b - >'a並且返回值將是實現'a(將是一個接口)並將調用轉發給duck的對象。

我在C#中使用Reflection.Emit完成了這個,但我想知道F#反射,引用或其他結構是否會使它變得更容易。

有關如何完成此任務的任何建議?

在閱讀Tims回答之后編輯 ,我想我會提供更多細節

當我寫關於使用引用來幫助時,我想到的是:

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

是將運算符轉換為函數的運算符,%expr是該方法的工作單元。 我能夠將表達式轉換為函數(我猜),但不知道如何

當然這不會完全成功,因為IInterface將是'我希望F#反射可能有一些方便的功能,以便我可以構建一個基於類型對象和一些函數值的類型

編輯作為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)

這是嘗試在F#中移植DCI的“教科書”示例。 源和目標是DCI角色。 這是一個想法,任何遵守特定合同的數據對象都可以發揮作用。 在這種情況下,合同很簡單。 source需要一個名為decreaseBalance的成員函數,而destination需要一個名為increaseBalance的成員函數。 我可以通過內聯和成員約束來實現這個特定情況。 但是我想編寫一組給出接口和對象的函數。 在這種情況下,它可以是源(作為對象)和

type sourceContract = 
   abstract decreaseBalance : decimal -> sourceContract

作為類型。 結果將是sourceContract類型的對象,它將方法調用傳遞給源對象上具有相同名稱的方法。

F#reflection( Microsoft.FSharp.Reflection )是一個圍繞普通System.Reflection API的F#友好包裝器,所以我認為它不會在這里添加任何內容。

引號無法定義新類型:(您需要定義一個新類型來執行基於接口的鴨子類型)

> <@ { 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仍然是這樣的方式。

編輯:

我希望F#reflection可能有一些方便的函數,這樣我就可以構建一個基於類型對象和一些函數值的類型

我擔心不會。 這是關於F#反射的文檔: http//msdn.microsoft.com/en-gb/library/ee353491.aspx

您可以使用F#PowerPack中的組件編譯F#引用。 所以我認為你可以使用引號在運行時生成和執行代碼。 如果你編寫一個代表函數的引用並編譯它,你將獲得一個可用於實現接口的函數值。 這是一個簡單的例子:

#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

您可以混合使用引用語法和對Expr調用,這樣可以更輕松地從基本塊組合代碼。 編譯有點愚蠢(目前),因此生成的代碼將不如通常的F#代碼那樣高效(但是你需要在你的情況下測量它)。

我不太清楚我明白你究竟想做什么,所以如果你能提供更多細節,我可以給出更具體的答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM