F#中的强类型但用户可扩展的集合?
我正在设计一个数据结构,用于与来自F的C#API交互。广义地说,它是组件的强类型集合(F#中的强类型但用户可扩展的集合?,f#,printf,F#,Printf,我正在设计一个数据结构,用于与来自F的C#API交互。广义地说,它是组件的强类型集合(ComponentCollection),其中组件可能具有不同的类型 第一个过程如下所示: module ComponentCollection = let add<'key when 'key :> ComponentKey> (key: 'key) (comp: Component<'key>) coll = { coll with Component
ComponentCollection
),其中组件可能具有不同的类型
第一个过程如下所示:
module ComponentCollection =
let add<'key when 'key :> ComponentKey> (key: 'key) (comp: Component<'key>) coll =
{ coll with Components =
coll.Components |> Map.add (key :> ComponentKey) (box comp) }
let tryFind<'key when 'key :> ComponentKey> (key: 'key) coll =
coll.Components
|> Map.tryFind (key :> ComponentKey)
|> Option.map (fun x -> x :?> Component<'key>)
类型Foo=
{
Foo:int
}
类型栏=
{
酒吧:字符串
}
类型ComponentCollection=
{
FoodComponents:地图
条形码组件:地图
}
模块组件集合=
让addFoo foo comp xs=
{xs with foomponents=xs.foomponents |>Map.add foo comp}
设addBar comp xs=
{xs with BarComponents=xs.BarComponents |>Map.add bar comp}
设tryFindFoo-foo-xs=
xs.foomponents |>Map.tryFind foo
设tryFindBar条xs=
xs.BarComponents |>Map.tryFind条
这种设计有两个问题:
addFoo
,addBar
,tryFindFoo
,…)ComponentCollection
,组件的类型将无法扩展,例如,用户无法添加QuxComponents:Map
本身开放系统
类型ComponentKey=
接口
继承不可比
结束
类型Foo=
{
Foo:int
}
使用接口组件键
类型栏=
{
酒吧:字符串
}
使用接口组件键
类型ComponentCollection=
{
组件:地图
}
模块组件集合=
设addFoo(foo:foo)(comp:Component)xs=
{xs with Components=xs.Components |>Map.add(foo:>ComponentKey)(comp:>obj)}
设addBar(bar:bar)(comp:Component)xs=
{xs with Components=xs.Components |>Map.add(条:>ComponentKey)(comp:>obj)}
设tryFindFoo(foo:foo)xs=
组件
|>Map.tryFind(foo:>ComponentKey)
|>Option.map(有趣的x->x:?>组件)//大假设!
设tryFindBar(bar:bar)xs=
组件
|>Map.tryFind(条:>组件键)
|>Option.map(有趣的x->x:?>组件)//大假设!
//用户可以轻松地在自己的代码中添加更多内容
如何设计
ComponentCollection
实现类型安全性和可扩展性?这个问题有几个层次,因此我将尝试给出一个回答,在不运行太长时间的情况下解决它的本质
实际上,您想要得到的是一个在键和值类型方面都是异构的映射。这不是F#type系统非常适合表示的东西,因为它需要对F#不具备的存在类型的支持。让我们暂时离开关键部分,谈谈价值观
在第二种方法中对值进行装箱通常是在集合中存储异构值的合理解决方案,也是人们经常进行的权衡。通过装箱和强制转换,您放弃了一些类型安全性,但是如果您通过将组件设置为私有/内部来限制对映射的访问,并确保它只能通过模块函数访问,则可以轻松地保持键类型与组件类型匹配的不变量
因此,您可以使用以下方法删除样板文件:
module ComponentCollection =
let add<'key when 'key :> ComponentKey> (key: 'key) (comp: Component<'key>) coll =
{ coll with Components =
coll.Components |> Map.add (key :> ComponentKey) (box comp) }
let tryFind<'key when 'key :> ComponentKey> (key: 'key) coll =
coll.Components
|> Map.tryFind (key :> ComponentKey)
|> Option.map (fun x -> x :?> Component<'key>)
模块组件集合=
让我们添加ComponentKey>(key:'key)(comp:componentmap.tryFind(key:>ComponentKey)
|>Option.map(fun x->x:?>component简单的回答是,在静态类型的函数语言中,通常通过参数多态性实现“可扩展性”…即泛型,因此如果您认为您可能希望在某个点“扩展”Foo,那么将Foo的所有出现作为一个参数,并对其进行约束(最低限度)为了给出无论在何处出现所需的行为,如果您将Foo扩展到Foo2,只要它满足约束,它就会编译并运行。