簡體   English   中英

如何使用 F# 在記錄結構上定義 fmap

[英]How to define a fmap on a record structure with F#

是否有可能為記錄創建一個 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應用多態。 我怎樣才能做到這一點?

歡迎邪惡類型的黑客!

一個明顯的解決方案是使用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]);}

另請參閱此相關問題 那里提出的案例更簡單,但問題是相同的。

也是相關的。

我同意@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) }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM