如何在通用高阶函数F#

这个问题似乎很愚蠢,但我在网上找不到任何类似的内容。所以我走了。

我需要使用类似Collection的结构,该结构应用作Map中的值。该集合具有泛型类型,并且映射应包含这些集合中的任何一种(int,boolean,string等)。鉴于这不可能直接实现(由于在Map定义中将通用类型限制为唯一值),我使用了一个辅助DU来匹配所有情况(假设它们很少)。我的问题是我想编写一个通用函数,该函数接收HOF并在类似于Collection的数据结构上执行,而与内部通用类型无关,但我不能;这是说明问题的简单代码:

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)之类的通用类型注释也不起作用。

有什么办法可以避免这种情况并保持该函数的通用精神?我可能会错过一些确实很明显的东西,但看不到。

谢谢!

编辑

好的,我继续了最近的几天的研究,尽管我计划提交有关我的特定域问题的更详细的问题,但我想分享我遇到的可能解决方案,并询问是否认为有他们中的一些或多或少是沙哑的。我将按照找到它们的顺序列出解决方案。

1。 WalternativE响应

每种可能的类型将函数传递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 question

的回应

3。隐藏的通用类型

从最后一个问题中,我发现了存在类型,并偶然发现了this article。仅使用有关通用类型的文章的第一部分就可以完成我想要的事情。

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

印象

我不知道哪个是最好的选择;我觉得第二个函数有点尴尬,因为每个函数都需要这种类型。无论添加了什么样板,我都真的很喜欢第三个(这篇文章很好地解释了,阅读起来也很有趣)。你有什么想法?

climb166 回答:如何在通用高阶函数F#

据我从给定的代码中可以看到的,您使用的代码之间的非明显差异可能是一个问题。

如果我们看

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

这似乎只是另一个毫无意义的变化,因为您最好直接用func调用l。因此,看来我们又回到了@WalternativE的结论。

顺便说一句,这根本不是第一次有人问这样的方式组合使用的泛型和DU,而且看起来它并不是故意的。也许有人比我更聪明。

本文链接:https://www.f2er.com/2958725.html

大家都在问