是否有可能为记录创建一个fmap,以便我可以应用相同的功能来记录类似bur不同类型的字段
假设我有一个记录字段类型Item
和一条记录X
和一个功能transform
type Item<'a, 'b> = Item of 'a * 'b
let transform (i: Item<'a, 'b>) : Item<'a, string> =
let (Item (x, y)) = i
Item (x, sprintf "%A" y)
type X<'a> = {
y: Item<'a, int>
z: Item<'a, bool>
}
with
member inline this.fmap(f) =
{
y = f this.y
z = f this.z
}
现在,行z = f this.z
会抱怨给定的类型应为Item<'a, int>
,但类型为Item<'a, bool>
。显然作为类型传感器
已决定函数f
的类型为Item<'a, int> -> Item<...>
,但我希望f
应用于多态。我怎么能这样做?
欢迎邪恶的黑客攻击!
答案 0 :(得分:2)
一个明显的解决方案是使用bimap
而不是fmap
,然后在调用者网站上使用两倍的函数:
type Item<'a, 'b> = Item of 'a * 'b
let transform (i: Item<'a, 'b>) : Item<'a, string> =
let (Item (x, y)) = i
Item (x, sprintf "%A" y)
type X<'a> = {
y: Item<'a, int>
z: Item<'a, bool>
}
with
member inline this.bimap(f, g) =
{
y = f this.y
z = g this.z
}
另一种选择(这里是邪恶的黑客攻击)不是通过一个函数,而是通过我称之为“Invokable”的函数。哪种函数用一个名为Invoke
的方法包装在一个类型中。像委托但是静态的东西。
这是一个例子。为简单起见,我使用$
代替Invoke
:
let inline fmap invokable ({y = y1; z = z1}) = {y = invokable $ y1; z = invokable $ z1}
type Id = Id with
static member ($) (Id, Item (a,b)) = Item (id a, id b)
type Default = Default with
static member ($) (Default, Item (a:'t,b:'u)) =
Item (Unchecked.defaultof<'t>, Unchecked.defaultof<'u>)
let a = {y = Item ('1', 2); z = Item ('3', true) }
let b = fmap Id a
let c = fmap Default a
现在问题是我无法想到许多其他有用的功能。你能吗?
否则,如果你使它更通用:
type X<'a, 'b, 'c> = {
y: Item<'a, 'b>
z: Item<'a, 'c>
}
然后你可以使用像这样的Invokable:
type ToList = ToList with static member ($) (ToList, Item (a,b)) = Item ([a], [b])
let d = fmap ToList a
// val d : X<char list,int list,bool list> = {y = Item (['1'],[2]);
z = Item (['3'],[true]);}
另见this related question。提出的案例比较简单,但问题是一样的。
this one也是相关的。
答案 1 :(得分:2)
我同意@Fyodor如果你需要表达一个多态参数,使用接口是最干净的解决方案:
type Item<'a, 'b> = Item of 'a * 'b
let transform (i: Item<'a, 'b>) : Item<'a, string> =
let (Item (x, y)) = i
Item (x, sprintf "%A" y)
type ITransform<'a,'x> = abstract Apply : Item<'a,'b> -> Item<'x,'b>
type X<'a> = {
y: Item<'a, int>
z: Item<'a, bool>
}
with
member inline this.fmap(f:ITransform<_,_>) =
{
y = f.Apply this.y
z = f.Apply this.z
}
{ y = Item(1,2); z = Item(3,true) }.fmap
{ new ITransform<_,_> with member __.Apply(Item(i,x)) = Item(i+1, x) }