This question is for amusement only. Please don't take this question too seriously.
I am currently learning F#, and I am interested to see if there is a concise way to define map4, using existing functions List.map2, List.map3, pipe forward/backward, forward/backward composition, etc.
i.e.
let map4 f a b c d = ......
map4 f [a1;a2] [b1;b2] [c1;c2] [d1;d2]
// output: [f(a1,b1,c1,d1); f(a2,b2,c2,d2)]
I can solve this recursively, or by defining a new operator (see the following URL)
http://www.fssnip.net/9W/title/nary-Seqmap-
http://call-with-cc-en.blogspot.sg/2009/04/applicative-functors-mapping-function.html
I can also solve this by combining List.map2 and List.map3, using partially applied functions f(a,b,?,?)
let map4 f a b c d =
List.map3 (fun g y -> g y) (List.map2 f a b) c d
I can try to shorten my code above using forward composition (and make it as abstract/confusing as possible)
let map4 f a =
List.map2 f a >> List.map3 id;;
// Output type: f:('a -> 'b -> 'c -> 'd -> 'e) ->
// a:'a list -> ('b list -> 'c list -> 'd list -> 'e list)
I would like to know if I can shorten it even further by getting rid of the "f" and "a", resulting in:
let map4 = ...... (* Use only List.map2, List.map3, |>, |<, >>, <<, etc.*) ..........
It will probably make it unnecessarily confusing, but it will be pretty cool. Thank you.
EDIT:
Adapting TheInnerLight's answer:
let inline (<!>) f xList = List.map f xList
let inline (<*>) gList xList = List.map2 (id) gList xList
let map4 f w x y z = f <!> w <*> x <*> y <*> z
let map5 f v w x y z = f <!> v <*> w <*> x <*> y <*> z
let map6 f u v w x y z = f <!> u <*> v <*> w <*> x <*> y <*> z
This is a good use for the applicative style of programming, i.e. using applicative functors.
Just define the apply
function and some helper operators:
module List =
// val apply : f:('a -> 'b) list -> x:'a list -> 'b list
let apply f x = List.map2 (fun f x -> f x) f x
// val inline ( <!> ) : f:('a -> 'b) -> x:'a list -> 'b list
let inline (<!>) f x = List.map f x
// val inline ( <*> ) : f:('a -> 'b) list -> x:'a list -> 'b list
let inline (<*>) f x = apply f x
Then use map and apply to define mapN
functions.
// val map2 : f:('a -> 'b -> 'c) -> x:'a list -> y:'b list -> 'c list
let map2 f x y = f <!> x <*> y
// val map3 : f:('a -> 'b -> 'c -> 'd) -> x:'a list -> y:'b list -> z:'c list -> 'd list
let map3 f x y z = f <!> x <*> y <*> z
// val map4 : f:('a -> 'b -> 'c -> 'd -> 'e) -> x:'a list -> y:'b list -> z:'c list -> a:'d list -> 'e list
let map4 f x y z a = f <!> x <*> y <*> z <*> a
// val map8 : f:('a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g -> 'h -> 'i) -> x:'a list -> y:'b list -> z:'c list -> a:'d list -> b:'e list -> c:'f list -> d:'g list -> e:'h list -> 'i list
let map8 f x y z a b c d e = f <!> x <*> y <*> z <*> a <*> b <*> c <*> d <*> e
As you can see, you can keep adding arguments to define arbitrary mapN
s to your heart's content.
Since the question specifically asks about using map2
or map3
, you can do this in the same style although it's a little less concise, e.g:
let map4_2 f x y z a = List.map2 f x y <*> z <*> a
let map4_3 f x y z a = List.map3 f x y z <*> a
Hopefully you get the idea.
As a small aside, I think it's worth noting that any monad is automatically an applicative functor, so there is a wide array of types you could use this pattern with, here is an Async
example.
module Async =
// val map : f:('a -> 'b) -> x:Async<'a> -> Async<'b>
let map f x = async.Bind(x, async.Return << f)
// val apply : f:Async<('a -> 'b)> -> x:Async<'a> -> Async<'b>
let apply f x = async.Bind(f, fun fe -> map fe x)
// val inline ( <!> ) : f:('a -> 'b) -> x:Async<'a> -> Async<'b>
let inline (<!>) f x = map f x
// val inline ( <*> ) : f:Async<('a -> 'b)> -> x:Async<'a> -> Async<'b>
let inline (<*>) f x = apply f x
// val map4 : f:('a -> 'b -> 'c -> 'd -> 'e) -> x:Async<'a> -> y:Async<'b> -> z:Async<'c> -> a:Async<'d> -> Async<'e>
let map4 f x y z a = f <!> x <*> y <*> z <*> a
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With