How to define fmap in a record structure using F #

it is possible to create fmap for records so that I can use the same function to write fields of similar types of different types.

Say I have a record field type Item and a record X and a transform function

 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 } 

now the string z = f this.z complains that this type should be Item<'a, int> , but its type is Item<'a, bool> . Obviously, the type infererrer decided that the function f is of type Item<'a, int> -> Item<...> , but I want f applied polymorphic. How can i do this?

Evil type hacks are welcome!

+5
source share
2 answers

The obvious solution is to use bimap instead of fmap , and then double the function on the callerโ€™s website:

 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 } 

Another alternative (hacking an evil type here) instead of passing a function passes what I call "Invokable", which is some kind of function wrapped in a type using a single method called Invoke . Something like a delegate, but static.

Here is an example. I use $ instead of Invoke for simplicity:

 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 

Now the problem is that I cannot come up with many other useful features. Can you?

Otherwise, if you make it more general:

 type X<'a, 'b, 'c> = { y: Item<'a, 'b> z: Item<'a, 'c> } 

then you can, for example, use Invokable as follows:

 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]);} 

See also this related question . The example presented there is simpler, but the problem is the same.

Also linked is this one .

+2
source

I agree with @Fyodor that using an interface is the cleanest solution if you need to express a polymorphic argument:

 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) } 
+2
source

Source: https://habr.com/ru/post/1261199/


All Articles