Fsharpplus: Deep map?

Created on 18 Dec 2019  路  18Comments  路  Source: fsprojects/FSharpPlus

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

enhancement question

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

All 18 comments

There is Compose which allows you to compose arbitrary functors

https://github.com/fsprojects/FSharpPlus/blob/33fa3411cb080fc70d8b4ad4f71e16d63bc82128/src/FSharpPlus/Monoids.fs#L93-L114

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 mapComp2 reads 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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cmeeren picture cmeeren  路  5Comments

Shmew picture Shmew  路  9Comments

robkuz picture robkuz  路  5Comments

gusty picture gusty  路  7Comments

cannorin picture cannorin  路  3Comments