-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added merge
(product) to Arrow
for arrows composition
#2063
Conversation
fix tests
…cats.arrow.Arrow
will need to add some mima exceptions exclude[ReversedMissingMethodProblem]("cats.arrow.Arrow.merge"),
exclude[ReversedMissingMethodProblem]("cats.arrow.Arrow.combineAndByPass"),
exclude[ReversedMissingMethodProblem]("cats.arrow.Arrow#Ops.merge"),
exclude[ReversedMissingMethodProblem]("cats.arrow.Arrow#Ops.combineAndByPass"),
exclude[ReversedMissingMethodProblem]("cats.arrow.Arrow#Ops.&&&"),
exclude[ReversedMissingMethodProblem]("cats.arrow.Arrow#Ops.-<"), to here |
I took the liberty to update your comment with some formatting. Hope you don't mind. |
…ombineAndByPass and relative syntax Ops
HI Kailuowang. I have committed the exclude directives. Do I need to do another pull request or is it ok for you? |
You are welcome! Everything related in one PR would be better. |
some errors in the doctest
To run doctest, |
Codecov Report
@@ Coverage Diff @@
## master #2063 +/- ##
==========================================
+ Coverage 94.66% 94.67% +<.01%
==========================================
Files 318 318
Lines 5380 5386 +6
Branches 112 216 +104
==========================================
+ Hits 5093 5099 +6
Misses 287 287
Continue to review full report at Codecov.
|
|
||
/** | ||
* Create a new computation `F` that apply f andThen biFork the result | ||
* one way is applied to g and the other pass through |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This description kind of implies that f
is only applied once, but actually it is applied twice right? the diagram is more accurate.
This implicit def catsSemigroupalForArrow[F[_, _], A](implicit F: Arrow[F]): Semigroupal[F[A, ?]] = new Semigroupal[F[A, ?]] {
def product[B, C](fb: F[A, B], fc: F[A, C]): F[A, (B, C)] =
F.andThen(F.lift((x: A) => (x, x)), F.split(fb, fc))
} let me punt on if we should add this though. |
I think this PR is good as is. I thought about renaming |
…n in doc was not visibile in browser. Deleted the last two rows of merge method scala doc: the schema is clear enough.
* | ||
*/ | ||
@simulacrum.op("-<", alias = true) | ||
def combineAndByPass[A, B, C](f: F[A, B], g: F[B, C]): F[A, (B, C)] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you explain the meaning behind this name? Should it be Bi
instead of By
? It makes me think of the word bypass, but I suspect you weren't going for that based on the camelCasing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry it should be 'bypass' without camelCase. I'm converting the camel to a dromedary ;-) and the I'll commit.
Thanks @marcobattaglia! I definitely like having the |
817831a
@ceedubs |
Actually, before I sign off this PR, I start to wonder what would be the difference between |
*/ | ||
@simulacrum.op("-<", alias = true) | ||
def combineAndBypass[A, B, C](f: F[A, B], g: F[B, C]): F[A, (B, C)] = { | ||
andThen(lift((x: A) => (x, x)), split(f, andThen(f, g))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One downside of this is that it calculates f(x)
twice. I think that for pretty much any arrow you would want to override this to reuse the result of f(x)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. I think I incline to not adding this one, and the default impl is just an alias to merge(f, f >>> g)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you are right. It is also the same of this one:
andThen(andThen( f, lift((x: B) => (x, x)) ), split(id, g))
This last one evicts the double calculation.
Essentially: input=> f(input) => out1=> (out1,out1)=> (out1,g(out1))=>(out1,out2)
Please consider if it's better to eliminate combineAndBypass because it is possible to use merge(f, f >>> g)
or if it is good to have combineAndBypass as a sort of shortcut.
I'll prepare the commit following your decision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mmm.... I think that also merge(f, f >>> g)
evaluates f twice, instead this one andThen(andThen( f, lift((x: B) => (x, x)) ), split(id, g))
evaluates f one time, so that it is not properly a simple shortcut althought the result is the same.
* | ||
*/ | ||
@simulacrum.op("-<", alias = true) | ||
def combineAndBypass[A, B, C](f: F[A, B], g: F[B, C]): F[A, (B, C)] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be andThen(f, merge (id, g))
right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Do you prefer to eliminate the combineAndBypass or to leave it as a simple shortcut? For me it's the same, I think that it depends on how cats is generally organized. I will commit following your opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tries to rewrite andThe(f, merge(d,g))
as something like andThen(f, catsSemigroupalForArrow.product(id,g))
and I've noticed that catscatsSemigroupalForArrow.product
is a little bit different than merge
infact the product change also the types of the resulting function.
For example the type of
catsSemigroupalForArrow.product( ((x:Int) => x * 2d), ((x:Double) => x * 5) )
is Double with Int => (Double, Double) , instead the type of `((x:Int) => x * 2d) merge ((x:Int) => x * 5)' is Int => (Double, Int). My question is: 'product' means also an operation on types, in the sense that it also work on the domain types of the function ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your catsSemigroupalForArrow.product( ((x:Int) => x * 2d), ((x:Double) => x * 5) )
is supprising. I didn't expect that to compile. We can discuss that #2078
If you don't mind I'd prefer removing combineAndBypass
. It's not providing much, typing f >>> (id &&& g)
isn't too much worse than f -< g
which arguably is more obscure to read. And it's a lot harder to remove a method than adding one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. I will prepare the commit to delete it. Thx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this capanion object: object Arrow { implicit def catsSemigroupalForArrow[F[_, _], A](implicit F: Arrow[F]): Semigroupal[F[A, ?]] = new Apply[F[A, ?]] { def map[B, C](fb: F[A, B])(f: B => C): F[A, C] = F.rmap(fb)(f) def ap[B, C](ff: F[A, B => C])(fb: F[A, B]): F[A, C] = F.rmap(F.andThen(F.lift((x: A) => (x, x)), F.split(ff, fb)))(tup => tup._1(tup._2)) }
compiles.
* | ||
* In the reference articles "Arrows are Promiscuous...", and in the corresponding Haskell | ||
* library `Control.Arrow`, this function is called `arr`. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hate to nitpick on this, but this format is unnecessarily changed away from the scala doc convention.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excuse me kailuowang, but I don't understand. What format is changed away? I don't know where I must correct the format, because I don't notice any difference.Please show me better.thx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The indentations of the * :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, it's a total nitpick. I think it's ready to merge.
merge
(product) to Arrow
for arrows composition
Ops! Space! Sorry, I've committed just now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much!
merge &&&
combineAndByPass -<
Create a new computation
F
that apply f andThen biFork the resultone way is applied to g and the other pass through
the result is a tuple
Example: