如何使用F#在记录结构上定义fmap

rob*_*kuz 5 polymorphism f# interface

是否有可能为记录创建一个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
        }
Run Code Online (Sandbox Code Playgroud)

现在该行z = f this.z抱怨给定的类型应该是Item<'a, int>其类型Item<'a, bool>.显然,类型推断器
已经确定函数f是类型Item<'a, int> -> Item<...>但是我想要f应用多态.我怎么能这样做?

邪恶的黑客欢迎!

Gus*_*Gus 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
        }
Run Code Online (Sandbox Code Playgroud)

另一种选择(这里是邪恶的类型黑客)是不传递函数,而是传递我所说的“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
Run Code Online (Sandbox Code Playgroud)

现在的问题是我想不出许多其他有用的功能。你可以吗?

否则,如果你让它更通用:

type X<'a, 'b, 'c> = {
    y: Item<'a, 'b>
    z: Item<'a, 'c>
}
Run Code Online (Sandbox Code Playgroud)

那么你可以像这样使用 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]);}
Run Code Online (Sandbox Code Playgroud)

另请参阅此相关问题。那里提出的案例更简单,但问题是相同的。

也是相关的。