Conversation
| val flatMap = | ||
| if (arity == 1) | ||
| s"def flatMap[Z](f: (${`A..N`}) => F[Z])(implicit flatMap: FlatMap[F]): F[Z] = flatMap.flatMap($tupleArgs)(f)" | ||
| else | ||
| s"def flatMapN[Z](f: (${`A..N`}) => F[Z])(implicit flatMap: FlatMap[F], semigroupal: Semigroupal[F]): F[Z] = flatMap.flatten(Semigroupal.map$arity($tupleArgs)(f))" | ||
|
|
There was a problem hiding this comment.
Looking into the Boilerplate code I noticed, that support for mapN is currently implemented in this way:
SemigroupalArityFunctionscontains a bunch of mapX (2 ≤ X ≤ 22) functions (along with other arity functions like imapX and contramapX.- These implementations are used then from all other arity syntax, including
TupleSemigroupalSyntax.
Shouldn't we follow the same pattern for flatMapN – i.e. add implementations for all flatMapX into SemigroupalArityFunctions in the first place and then make use of it from everywhere else?
I am not certain about it though, just an observation.
There was a problem hiding this comment.
Also there's the ApplyArityFunctions trait that adds the mapX syntax directly to the Apply typeclass.
Curious, shouldn't we create a FlatMapArityFunctions trait (it does not exist yet) and add flatMapX method directly into the FlatMap typeclass as well?
There was a problem hiding this comment.
All excellent questions! My reason for doing it as it is is the number of methods. The initial concern with regards to adding .flatMapN was jar size, since adding arity methods adds a lot of code. Because of that I opted to only add it where it's - in my opinion - most useful, which is the tuple syntax. I personally have never used Apply[F].mapN, so I don't know if it's particularly useful to add a FlatMap[F].flatMapN. But if the consensus says otherwise I'm happy to change that.
There was a problem hiding this comment.
Agree, jar size is a concern. Did you try by chance to measure how much the resulting jars get increased in size comparing to the base version? May be it adds not too much...
There was a problem hiding this comment.
I did not actually. I'll give it a shot today/tomorrow and see how it compares.
| val tfabc = mock[(F[A], F[B], F[C])] | ||
| val ff = mock[(A, B, C) => F[Z]] | ||
|
|
||
| tfabc.flatMapN(ff) |
There was a problem hiding this comment.
Tests are always good, I personally appreciate it. The only notice: there's an if condition in your code, but this test only checks a branch for arity > 1. Although I don't think it is absolutely necessary, but it would be nice if you could add a test for the arity = 1 branch.
There was a problem hiding this comment.
Yup, I think this is easy to add.
There was a problem hiding this comment.
I would really like to have this addressed (since the syntax for Tuple1 seems to be slightly different), but I'm not going to hold the entire PR on just because of this concern only.
|
Personally, I frequently find myself wanting In theory, we add methods to typeclasses (and syntax merely delegates to those) so that instances can override those with more efficient implementations (you can't override syntax extensions :) In practice, not sure if it makes a meaningful difference here. But still I think that adding these methods to a |
|
I'll take another look when I can |
15a9806 to
89f1bc0
Compare
|
Rebased on latest main, added |
89f1bc0 to
980c53e
Compare
project/Boilerplate.scala
Outdated
| | * @groupprio FlatMapArity 999 | ||
| | */ | ||
| |trait FlatMapArityFunctions[F[_]] { self: FlatMap[F] => | ||
| - /** @group MapArity */ |
There was a problem hiding this comment.
| - /** @group MapArity */ | |
| - /** @group FlatMapArity */ |
| val flatMap = | ||
| if (arity == 1) | ||
| s"def flatMap[Z](f: (${`A..N`}) => F[Z])(implicit flatMap: FlatMap[F]): F[Z] = flatMap.flatMap($tupleArgs)(f)" | ||
| else | ||
| s"def flatMapN[Z](f: (${`A..N`}) => F[Z])(implicit flatMap: FlatMap[F]): F[Z] = flatMap.flatMap$arity($tupleArgs)(f)" |
There was a problem hiding this comment.
So I haven't been able to wrap my head around whether this technically belongs in TupleSemigroupalSyntax. It's certainly the easiest place to put it, and it works, I'm just confused by the "semigroupal" in the name :)
There was a problem hiding this comment.
Semigroupal captures the idea of composing independent effectful values.
Ah, ok that makes sense :)
armanbilge
left a comment
There was a problem hiding this comment.
Thank you very much, I think this looks good!
satorg
left a comment
There was a problem hiding this comment.
lgtm, thanks a lot. The test for the arity = 1 case would be appreciated though.
|
I'll add the test, but I'm honestly not sure why there even is a |
danicheg
left a comment
There was a problem hiding this comment.
I was dreaming of flatMapN for years. It's nice to see this happening!
armanbilge
left a comment
There was a problem hiding this comment.
I'll add the test, but I'm honestly not sure why there even is a
Tuple1syntax. Is that useful?
Agree, pretty dubious about this 😆 but it's good for completeness. Thanks!
Perhaps just for completeness and consistency with other syntax, I guess. |
Hello 👋
As promised here is
.flatMapN. Took me a little while to get my head aroundBoilerplate. Let me know if this works for you, or if you need anything else.resolves #4003