I often write several levels of map: map (map (...
Is it possible to implement a generic "deepMap" that maps the innermost or arbitrarily nested functors?
Possible inspiration: http://okmij.org/ftp/Haskell/typecast.html#deepest-functor
There is Compose which allows you to compose arbitrary functors
But what you want is to avoid wrapping / unwrapping.
We could add a set of map2 mapN functions, additionally/alternatively we could add some funny versions of applicative operators like <!!> and <**>.
Definitely it's something to consider.
A full generic deepmap over an arbitrary number of functors is quite complex to encode, it might be possible I have to give it a try.
Then if it works we have to see how is type inference, my feeling is that with the current state of the F# compiler it won't be amazing.
I was playing a bit today with Compose and it's not that bad, but wrapping and unwrapping is a bit tedious as always.
So, I'm leaning towards adding <!!> for deep map and <**> for deep apply. Well, not really deep as the link provided, just 2 levels (eventually we can triple them, but don't come to me saying that the compiler is killing your computer ;)
They both correspond exactly to <!> and <*> implementations of Compose.
Does anybody know if those operators have other meanings in other libs, applications? (not counting Space Invaders)
<**>
https://hackage.haskell.org/package/base-4.12.0.0/docs/Control-Applicative.html#v:-60--42--42--62-
Same as <*> but with arguments flipped
Yes, I saw it. But I don't think it's used in F# to flip <*> and I don't think we need it that much.
Yes, my feeling as well. Though question is how common the case of deep map is?
Perhaps a module function in order to hold off on defining a new operator?
Would be great to have a module function. It's not a replacement for an operator since operators are for infix usage, but personally I need a module function more than I need an operator.
By the way, if this is much heavier on the compiler/CPU than normal nested maps (even in client code), I might not use it at all. We'll see how heavy it is.
Could you guys show a code fragment where this operator/module function would be used.
I proposed infix operators, since applicatives are more idiomatic as infix, actually there's no generic <*> module function.
It shouldn't be heavier than currently Compose is, maybe even a bit lighter.
From the top of my head:
```f#
async { return (Some 1) }
|> map2 ((+) 3)
Instead of:
```f#
async { return (Some 1) }
|> map (map ((+) 3))
That's very similar to the sample code I just added for Compose, see http://fsprojects.github.io/FSharpPlus/type-compose.html#Examples
So right now you can do:
async { return (Some 1) }
|> Compose |> map ((+) 3) |> Compose.run
With operators you would be able to do:
async { return (Some 1) }
|> (<!!>) ((+) 3)
But, it would be more natural to do:
((+) 3) <!!> async { return (Some 1) }
and it would be more flexible since it would allow you to do:
(+) <!!> async { return (Some 1) } <**> async { return (Some 2) }
Though, I agree that for the 2nd sample, a named function would look better. The problem is which name? map2 already exists in F# which is actually map with 2 functor arguments (but not composed).
So, it should be called something like mapSquared.
Btw: if I were you, I would have wrote your code like this:
async { return (Some 1) }
|> (map >> map) ((+) 3)
I think it reads better.
What about naming it mapComp2 etc.?
My example was a bit contrived; I'll see if I can find a real example.
My example was a bit contrived; I'll see if I can find a real example.
Please !
I wonder if mapComp2 reads better than (map >> map)
I wonder if
mapComp2reads better than(map >> map)
Fair point.
@cmeeran I wonder if it's specific combinations of functors that is more common?
I have started using ResultT with Async, which means I can just use map. If we create a specialized type for this, you'd save on wrap/unwrap in the ResultT and could add some conveniences...
Only other case I could find in my code was "Async.map (Array.map" which was after an Async.Parallel. Interested in your cases!
I also am unsure what to call map >> map -- must say it's first time I've considered it as I also tend to |> map (map ... but I didn't mind as it helps when thinking of the layers.
Okay, here's a simplified example of the only place I currently have map (map. It may not make much sense. I've tried indicating function signatures by their name. (It's a continuation of the refactoring Mark Seemann did in this post.)
```f#
Xoption
|> traverse (XtoAsyncBool)
|> bind (
boolOptionToResultAB
either
(AtoAsyncResultUnitC >> map (map (fun () -> valueOfD)))
BtoD
)
As you say @gusty, the `map (map` line can be rewritten to
```f#
AtoAsyncResultUnitC >> (map >> map) (fun () -> valueOfD)
@adz I haven't really understood monad transformers (in FSharpPlus or otherwise). Can you given a convincing example of using ResultT with Async? Is the added boilerplate (wrapping/unwrapping) worth it?
You are dealing with two monads - async and result... So monad transformers are just a type to help combine them.
I have ResultT<Async<Result<_>>> because I am combining with Async. It seems a bit backwards as the Result is inner. The ResultT can combine with any other monad.
I wrote notes as I started to feel comfortable with them, because I also struggled to comprehend them for a long time.... See here
http://adzdavies.blogspot.com/2019/12/monadtransformers-lets-look-at-resultt.html
Most helpful comment
You are dealing with two monads - async and result... So monad transformers are just a type to help combine them.
I have
ResultT<Async<Result<_>>>because I am combining with Async. It seems a bit backwards as the Result is inner. The ResultT can combine with any other monad.I wrote notes as I started to feel comfortable with them, because I also struggled to comprehend them for a long time.... See here
http://adzdavies.blogspot.com/2019/12/monadtransformers-lets-look-at-resultt.html