我正在尝试学习TypeShape库,并根据自述中的片段玩IShapeMember
类型IShapeMember<'DeclaringType,'字段>abstract Get:'声明类型->'领域抽象集:'DeclaringType->'字段->'DeclaringType
这是我的代码,旨在推广"setter"函数
let setMemberValue (value: 'b) (target: 'c) (shp: IShapeMember<'c>) =
shp.Accept { new IMemberVisitor<'c,'c> with
member x.Visit (m: ShapeMember<'c,'b>) = m.Set target value}
但这给了我编译错误:这段代码不够通用。类型变量"a"无法泛化,因为它将脱离其作用域
(顺便说一句,这不是错误的类型,它确实抱怨变量"a…"(
我试过各种类型的争吵,但我无法理解我做错了什么。
我不是TypeShape专家,但我认为这里的问题是你没有遵守Visit
函数的签名,即:
type IMemberVisitor<'TRecord, 'R> =
abstract Visit<'Field> : ShapeMember<'TRecord, 'Field> -> 'R
请注意,'Field
类型参数由调用Visit
的用户决定,而不是由您决定。因此,在对Set
的调用中使用外部value
的尝试不够通用。相反,您需要一种方法来从Visit
函数内的中获得'Field
的实例,这样它就可以与调用方想要的任何'Field
类型一起使用。这说起来容易做起来难,但如果你想从一个微不足道的例子开始,这将至少编译:
let visitor target =
{
new IMemberVisitor<'TRecord, 'R> with
member x.Visit (m: ShapeMember<'TRecord,'Field>) =
let value = Unchecked.defaultof<'Field>
let target' = m.Set target value
Unchecked.defaultof<'R>
}
这里还有一个从Visit
中调用Set
的示例,但它更复杂。我认为没有任何方法既简单又有用。
let setMemberValue (value: 'b) (target: 'c) (shp: IShapeMember<'c>) =
shp.Accept { new IMemberVisitor<'c,'c> with
member x.Visit (m: ShapeMember<'c,'d>) =
if typeof<'b> = typeof<'a> then //or convert 'b -> 'd function
m.Set target (box value :?> 'd)
else
failwith "The supplied type does not match the type of the target field"
}
将所提供的value
装箱并铸造为类型'd
起到了作用。如果调用方提供的值与正在设置的字段的值不匹配,则所有内容都以某种形式的类型检查进行包装