-
-
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
Changes from 13 commits
28fe55a
c2e451f
29c878c
4d88b6c
fa6aa46
4f1c522
d7d0d47
5f1e1d6
f80bcb5
87ff6a6
db2d224
2c0bc19
817831a
91dfe26
17c6a9e
c53e368
ef5996f
5bd2889
4caa33e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,4 +45,50 @@ import simulacrum.typeclass | |
@simulacrum.op("***", alias = true) | ||
def split[A, B, C, D](f: F[A, B], g: F[C, D]): F[(A, C), (B, D)] = | ||
andThen(first(f), second(g)) | ||
|
||
/** | ||
* Create a new computation `F` that merge outputs of `f` and `g` both having the same input | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.implicits._ | ||
* scala> val addEmpty: Int => Int = _ + 0 | ||
* scala> val multiplyEmpty: Int => Double= _ * 1d | ||
* scala> val f: Int => (Int, Double) = addEmpty &&& multiplyEmpty | ||
* scala> f(1) | ||
* res0: (Int, Double) = (1,1.0) | ||
* }}} | ||
* | ||
* Note that the arrow laws do not guarantee the non-interference between the _effects_ of | ||
* `f` and `g` in the context of F. This means that `f &&& g` may not be equivalent to `g &&& f`. | ||
*/ | ||
@simulacrum.op("&&&", alias = true) | ||
def merge[A, B, C](f: F[A, B], g: F[A, C]): F[A, (B, C)] = { | ||
andThen(lift((x: A) => (x, x)), split(f, g)) | ||
} | ||
|
||
/** | ||
* Create a new computation `F` that apply f andThen biforks the result. | ||
* On one way it is applied to g and on the other it is passed through. | ||
* The final result is a tuple | ||
* <br/><pre> | ||
* /--out1 = f(input) ----\ | ||
* input -->{ }--->(out1,out2) | ||
* \--out2 = g(f(input))--/ | ||
* </pre> | ||
* Example: | ||
* {{{ | ||
* scala> import cats.implicits._ | ||
* scala> val twoTimes: Int => Double = _ * 2d | ||
* scala> val fiveTimes: Double => Double= _ * 5 | ||
* scala> val f: Int => (Double, Double) = twoTimes -< fiveTimes | ||
* scala> f(2) | ||
* res0: (Double, Double) = (4.0,20.0) | ||
* }}} | ||
* | ||
*/ | ||
@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 commentThe reason will be displayed to describe this comment to others. Learn more. One downside of this is that it calculates There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes you are right. It is also the same of this one: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mmm.... I think that also |
||
} | ||
} |
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 likeandThen(f, catsSemigroupalForArrow.product(id,g))
and I've noticed thatcatscatsSemigroupalForArrow.product
is a little bit different thanmerge
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 #2078If you don't mind I'd prefer removing
combineAndBypass
. It's not providing much, typingf >>> (id &&& g)
isn't too much worse thanf -< 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.