2014-03-24 64 views
7

我認爲F#函數和System.Func之間的轉換必須手動完成,但似乎有編譯器(有時)會爲您執行它的情況。而當它出現問題的錯誤消息是不準確的:代表/ Func轉換和誤導性編譯器錯誤消息

module Foo = 
    let dict = new System.Collections.Generic.Dictionary<string, System.Func<obj,obj>>() 

    let f (x:obj) = x 

    do 
     // Question 1: why does this compile without explicit type conversion? 
     dict.["foo"] <- fun (x:obj) -> x 
     // Question 2: given that the above line compiles, why does this fail? 
     dict.["bar"] <- f 

最後一行編譯失敗,錯誤是:

This expression was expected to have type 
    System.Func<obj,obj>  
but here has type 
    'a -> obj 

顯然,功能f不具有簽名'a > obj。如果F#3.1編譯器對第一個字典賦值感到滿意,那爲什麼不是第二個呢?

+0

我想這是一個轉換不是自動的情況,但這個錯誤並不是很有幫助,但可能在技術上仍然是正確的。 –

+0

我猜想只有lambda會自動轉換。 – MisterMetaphor

+0

@MisterMetaphor:correct:http://stackoverflow.com/questions/3392000/interop-between-f-and-c-sharp-lambdas – Mau

回答

5

應該解釋規範的一部分這是8.13.7 Type Directed Conversions at Member Invocations。簡而言之,在調用成員時,將會應用從F#函數到委託的自動轉換。不幸的是,規範有點不清楚;從措辭看來,這種轉換可能適用於任何函數表達式,但實際上它似乎只適用於匿名函數表達式。

該規範也有點過時;在F#3.0類型定向轉換中,還可以轉換爲System.Linq.Expressions.Expression<SomeDelegateType>

編輯

在尋找與F#團隊過去的一些信件,我想我已經找到了一個轉換如何能得到應用到非句法功能表達。爲了完整性,我將它包含在這裏,但這有點奇怪,所以對於大多數目的,您應該考慮規則是隻有語法函數纔會應用類型定向轉換。

例外情況是重載解析可能導致轉換函數類型的任意表達式;這部分由部分14.4 Method Application Resolution解釋,雖然它很密集,但仍不完全清楚。基本上,當存在多個重載時,參數表達式只有詳述了;當只有一個候選方法時,參數類型是針對未經討論的論點而提出的(注意:這並不是顯而易見的,就轉換是否適用而言,這實際上很重要,但它在經驗上確實很重要)。下面是一個演示這個例外的例子:

type T = 
    static member M(i:int) = "first overload" 
    static member M(f:System.Func<int,int>) = "second overload" 

let f i = i + 1 

T.M f |> printfn "%s" 
+1

不錯。顯然,規範專門用來說明只有lambda表達式才被提升;看到[這個答案](http://stackoverflow.com/questions/2783450/creating-delegates-with-lambda-expressions-in-f)。我想知道現在轉換的「其他函數值參數」可能是什麼? –

+0

執行此操作的編譯器部分似乎是[this bit](https://github.com/fsharp/fsharp/blob/master/src/fsharp/typrelns.fs#L2202)。儘管如此,我在解決「不是lambda」的情況下遇到了什麼困難。有人能解釋這裏發生了什麼嗎?除了自動轉換的lambda外,還有其他的東西嗎? –

+1

@SørenDebois - 看我的編輯。 – kvb

0

編輯:這個答案只解釋了對'a -> obj的神祕促銷。 @kvb指出,在OP的例子仍然替換objint不起作用,因此促進本身對觀察到的行爲本身的解釋不足。


爲了提高靈活性,F#的類型elaborator可以在一定條件下促進從f : SomeType -> OtherTypef<'a where 'a :> SomeType> : 'a -> OtherType命名函數。這是爲了減少對upcasts的需求。 (參見spec. 14.4.2

問題2第一:

dict["bar"] <- f      (* Why does this fail? *) 

由於f是 「命名的函數」,它的類型是從f : obj -> obj以下秒促進。 14.4.2對錶面上較少限制的f<'a where 'a :> obj> : 'a -> obj。但是這種類型與System.Func<obj, obj>不兼容。

問題1:

dict["foo"] <- fun (x:obj) -> x  (* Why doesn't this, then? *) 

這是很好的,因爲匿名函數沒有命名,因此秒。 14.4.2不適用。這種類型從來沒有從obj -> obj晉升,所以適合。


我們可以觀察到的解釋行爲表現出以下14.4.2:

> let f = id : obj -> obj 
val f : (obj -> obj)       (* Ok, f has type obj -> obj *) 
> f 
val it : ('a -> obj) = <fun:[email protected]>  (* f promoted when used. *) 

(解釋器不會輸出約束obj

+2

這是不對的;不涉及自動提升(嘗試相同的例子,但函數從'int'到'int')。 – kvb

+0

呵呵。我更新了答案。 –

相關問題