2016-12-13 27 views
5

記錄結構的FMAP有創造紀錄的FMAP,這樣我可以將同樣的功能來記錄類似鑽不同類型的字段如何定義與F#

讓說我有一個記錄的可能性字段類型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>類型。很明顯,因爲類型傳入者
已經決定函數fItem<'a, int> -> Item<...>類型,但是我想要f被應用到多態。我怎樣才能做到這一點?

邪惡類型的黑客是受歡迎的!

+1

功能通用性只能在定義。一旦你把它變成「價值」並開始傳遞它,它就會消失。如果您需要保持通用性,請使用界面。 –

+0

所以f應該是任何函數'a - >'a'其中'a可以是一個字符串或bool?除了''id''之外,我可以想象的功能並不多。 – Gustavo

回答

2

一個顯而易見的解決方案是使用bimap而不是fmap然後WIRTE在調用者的網站兩次的功能:

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> 
} 

,那麼你可以使用例如一個可調用這樣的:

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是相關的。

+0

順便說一聲我不知道你是否已經注意到了,但是FsControl中的大部分多態函數都是Invokables;) – Gustavo

+0

首先要感謝Gustavo Fyodor和@kvb--我認爲這次對我來說確實是點擊了。所以**解決方案**是創建一些多態的委託類型,而不是我的func和一些關聯的方法,並在調用/應用gustavo的提議稍微精簡時調用此方法。 但是 - 你們都不覺得這有點重嗎?我的意思是將多態函數作爲第一類函數參數推出並不是特別深奧的東西!爲什麼類型推理者會干涉這種情況?只是一個修辭問題;-) – robkuz

+0

@robkuz不只是F#類型的推論。我猜所有語言都像Hindly-miner那樣喜歡類型推斷。即使是Haskell,但如果你閱讀我發給你的第一個鏈接,似乎F#在這些情況下比Haskell更加靈活;) – Gustavo

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) } 
+0

我在這裏有一個後續問題https://stackoverflow.com/questions/45088899/how-can-i-map-over-a-record-using-interfaces – robkuz