簡體   English   中英

如何繞過對通用高階函數 F# 的類型推斷限制

[英]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。 我將按照找到的順序列出解決方案。

1. 替代反應

每個可能的類型將函數傳遞 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])

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 問題的回應

3. 隱藏的通用類型

從最后一個問題,我發現了 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.

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