F#成员对元组的约束

时间:2011-07-28 09:41:12

标签: generics f# constraints

当我意识到我需要一个额外的价值时,我通常会写一个“哦耶”的时刻写F#。这通常可以通过向传递的元组添加另一个值来轻松完成。但是,这意味着各种地图/排序/收集/等。需要更新,特别是函数fst / snd只适用于长度为2的元组。

这不是一个大问题,但在探索性开发过程中它很烦人,但我会写一个助手来减轻烦恼:

let inline get2 (t:^a) = (^a : (member get_Item2 : unit -> string) (t, ()))
let inline get2a (t:^a) = (^a : (member Item2 : string) t)

但是,两个版本都不起作用。第一个get2将不会编译,“预期的1个表达式,得到2”。第二个get2a将编译,但随后不能用于元组:“类型'(int * string)'不支持任何名为'get_Item2'的运算符。”

有没有办法做到这一点,不涉及大量的重载?带有嘈杂OverloadID注释的 (F#2.0中不需要注释)

2 个答案:

答案 0 :(得分:8)

F#元组的ItemX静态约束不起作用的原因是因为System.Tuple<_,...,_>只是元组的编码形式,而不是编译器使用的静态表示。请参阅规范中的6.3.2 Tuple Expressions

但是,通过一些工作,您可以获得给定元组的运行时编码,如下所示:

open System
//like get2a but generic return type
let inline get2b (t:^a) = (^a : (member Item2 : 'b) t)

let x = (1,2)
let y = (1,2,3)

get2b (box x :?> Tuple<int,int>)
get2b (box y :?> Tuple<int,int,int>)

答案 1 :(得分:3)

你可以使用反射来做到这一点:

let inline get (t:^a) = t.GetType().GetProperty("Item2").GetValue(t,null) :?> string

另外,我建议元组实际上不是一个很好的数据结构来传递数据,它们在小型操作的函数中可能很有用,但是如果结构经常发生变化,元组可能会非常痛苦使用