[英]How to bypass type inference limitation on a generic higher order function F#
這個問題看起來很愚蠢,但我在網上找不到類似的東西。 所以我來了。
我需要使用類似 Collection 的結構,該結構應該用作 Map 中的值。 這個集合有一個通用類型,映射應該包含任何類型的這些集合(int、boolean、string 等)。 鑒於這是不可能直接實現的(因為泛型類型被限制為 Map 定義中的唯一值)我使用了一個輔助 DU 來匹配所有情況,因為它們很少。 我的問題是我想編寫一個通用函數來接收 HOF 並在類集合數據結構上執行,而不管內部通用類型如何......但我不能; 這是示例問題的簡單代碼:
type EspecificList =
| IntList of int list
| BoolList of bool list
let listLength = function
| IntList list -> List.length list
| BoolList list -> List.length list
let listGenericFunc func = function
| IntList list -> func list
| BoolList list -> func list
listLength 按預期工作,但 listGenericFunc 沒有; func
專門用於第一種類型,而在第二種類型上會出現錯誤:添加像(func: List<'a> -> 'b)
這樣的泛型類型注釋也不起作用。
有沒有辦法避免這種情況並保持功能的通用精神? 我可能遺漏了一些非常明顯的東西,但我看不到它。
提前致謝!
好的,我在過去幾天繼續我的研究,雖然我計划提交一個關於我的特定域問題的更詳細的問題,但我想分享我遇到的可能的解決方案,並詢問您是否認為其中任何一個是更好或更 FSharpish。 我將按照找到的順序列出解決方案。
每個可能的類型將函數傳遞 n 次; 帶或不帶類型注釋
let listGenericFunc (func: bool list -> 'b) (func': int list -> 'b) = function
| BoolList list -> func list
| IntList list -> func' list
listGenericFunc List.length List.length (IntList [1;2])
使用靜態“apply”成員並為每個可能的函數定義類型以及“applyer”內聯函數。
type ListLength = ListLength with static member ( $ ) (ListLength, x) = x |> List.length
let inline applier f x = f $ x
let listGenericFuncInline func = function
| IntList list -> applier func list
| BoolList list -> applier func list
listGenericFuncInline ListLength (IntList [1; 2; 3]) // return 3
listGenericFuncInline ListLength (BoolList [true; false]) // return 2
這是對這個特定SO 問題的回應
從最后一個問題,我發現了 Existential 類型並稍微搜索了一下我偶然發現了這篇文章。 僅使用有關通用類型的帖子的第一部分就可以完成我想要的。
type UListFuncs<'ret> = abstract member Eval<'a> : ('a list) -> 'ret
let listLength : UListFuncs<int> =
{ new UListFuncs<int> with
member __.Eval<'a> (x : 'a list) = x |> List.length }
let listGenericFuncUniversal (func : UListFuncs<'a>) = function
| IntList list -> func.Eval list
| BoolList list -> func.Eval list
listGenericFuncUniversal listLength (IntList [1; 2; 3]) // return 3
listGenericFuncUniversal listLength (BoolList [true; false]) // return 2
我不知道哪一個是最好的選擇; 我覺得第二個有點別扭,因為每個函數都需要類型; 無論添加的樣板如何,我都非常喜歡第三個(這篇文章解釋得很好,讀起來也很有趣)。 你怎么看?
據我所知,從給定的代碼中可以看出,您使用的代碼可能存在非明顯差異的問題。
如果我們看
let listLength = function
| IntList list -> List.length list
| BoolList list -> List.length list
您將看到對每個匹配案例對List.length
不同調用。 List.length
函數是通用的,編譯器可以在每個調用站點上找到正確的類型參數。
let listGenericFunc func = function
| IntList list -> func list
| BoolList list -> func list
另一方面,這個功能有點棘手。 編譯器將嘗試解析為正確的類型,但無法執行此操作,因為類型參數只有一種“調用位置”和兩種不同的情況。 它將綁定到第一個案例並告訴您第二個案例不匹配。 如果您更改匹配案例的順序,您將看到編譯器將相應地更改類型簽名。
您可以做的一件事(我不建議這樣做)是在應用該函數之前將兩種情況設為相同的類型。
let listGenericFunc' (func: obj list -> 'b) (esp : EspecificList) =
match esp with
| BoolList list -> list |> List.map box |> func
| IntList list -> list |> List.map box |> func
這將起作用,因為您的func
將始終具有obj list -> b
形式。 不過,這不是很好。 我寧願將您使用的 HOF 更改為對每種情況都有一個擬合函數(它們也可以是相同的通用函數 - 這樣就不會導致重復)。
let listGenericFunc (func: bool list -> 'b) (func': int list -> 'b) = function
| BoolList list -> func list
| IntList list -> func' list
listGenericFunc List.length List.length (IntList [1;2])
最終,使用數據結構的痛苦可能表明可能有更好的解決方案。 不過,這取決於您非常具體的需求,而且您很可能會在迭代項目時發現這一點。
let listGenericFunc (func: EspecificList -> 'a) (l: EspecificList) =
match l with
| IntList _ -> func l
| BoolList _ -> func l
這似乎只是另一個毫無意義的變化,因為您不妨直接使用l
調用func
。 所以看起來我們又回到了@WalternativE 的結論。
順便說一句,這並不是第一次有人詢問以這種方式組合泛型和 DU 的問題,而且看起來似乎並非如此。 也許比我更聰明的人可以詳細說明。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.