2012-08-02 78 views
0

假設你要編寫一個函數「根」與由一個類型列表給予了簽名生成功能:函數生成任意簽名

let gen (sig:Type list) = .... 

let sig = [ typeof<int>; typeof<int[]>; typeof<float> ] 
let func = gen sig 
then func is of type: int -> int[] -> float -> unit 

我想到了兩個可能的解決方案:

a)使用反射發射,但我不知道這樣我們可以使智能感知工作? 反射發射似乎創建新的.net代碼,IntelliSense可能看不到,所以它可能無法在編譯時驗證代碼。

b)使用類型提供程序,但我擔心太重,可能不實際。

c)使用泛型,如gen<'T1> (sig:Type list) : 'T2 -> unit,這可能需要遞歸調用,但我沒有想到如何做到這一點。

我更喜歡方法c),因爲它是輕量級的,並且可以在編譯時檢查。還是有其他方法?

+1

你什麼時候做這一代?運行?編譯時(如果是這樣,編碼?到DLL?)? – 2012-08-02 06:45:12

+0

出於好奇,你最終的目標是什麼? – nicolas 2012-08-04 13:46:15

回答

0

如果在運行程序之前,您已經擁有生成代碼的所有必要信息,則可以使用Reflection.Emit生成一個包含函數的DLL作爲預處理步驟,並在您的主項目中引用該函數。這樣你就可以獲得智能感知。如果,OTOH,你只能在運行時知道你的簽名,你可以使用動態方法(http://msdn.microsoft.com/en-us/library/system.reflection.emit.dynamicmethod.aspx),但這樣你顯然不會感覺到智能。

關於選項b)和c):我沒有嘗試過前者,但它仍然是編譯時操作,而後者在F#中不可行 - 語言沒有可變參數函數,printfn &合。在編譯器中烘焙。

1

你已經問過如何用一些給定的簽名來生成函數,但是問題忽略了關於函數體的觀點。

假設你有一些方法來設置這個身體,一個可能的解決方案是這樣的(警告:慢,因爲它在內部使用反射):

module F = 
    type FST = Microsoft.FSharp.Reflection.FSharpType 
    type FSV = Microsoft.FSharp.Reflection.FSharpValue 

    let mkFunction (handler : obj list -> obj) : 'T = 
     if not (FST.IsFunction typeof<'T>) then failwith "Function type expected" 
     let rec chain args ty = 
      let dty, rty = FST.GetFunctionElements ty 
      let impl = 
       if FST.IsFunction rty then 
        fun o -> chain (o::args) rty 
       else 
        fun o -> handler(List.rev (o::args)) 
      FSV.MakeFunction(ty, impl) 
     chain [] typeof<'T> :?> 'T 
let f : int -> (int -> int) -> string = 
    F.mkFunction <| 
     fun [:? int as a; :? (int -> int) as f] -> 
      box (string (f a)) 

如果要使用系統來表示所需的簽名。類型列表 - 說再見了intellenceence