这个问题似乎很愚蠢,但我在网上找不到任何类似的内容。所以我走了。
我需要使用类似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)
之类的泛型类型注释中也不起作用。
有什么方法可以避免这种情况并保持该函数的通用精神?我可能缺少一些非常明显的东西,但我看不到。
提前感谢!
[好吧,我在过去的几天里继续了我的研究,尽管我计划提交有关我的特定领域问题的更详细的问题,但我想分享我遇到的可能解决方案,并询问您是否认为他们或多或少是沙哑的。我将按照找到它们的顺序列出解决方案。
对每种可能的类型将函数传递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 question的回应
[从上一个问题开始,我发现了存在类型,然后我偶然发现了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
我不知道哪个是最好的选择;我觉得第二个函数有点尴尬,因为每个函数都需要这种类型。无论添加了什么样板,我都真的很喜欢第三个(这篇文章很好地解释了,阅读起来也很有趣)。您有什么想法?
据我从给定的代码中可以看到的,您使用的代码之间的非明显差异可能是一个问题。
如果我们看
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的结论。
Btw,这根本不是第一次有人问过以这种方式组合使用的泛型和DU,而且看起来这并非本来就应该如此。也许有人比我更聪明。