-
Notifications
You must be signed in to change notification settings - Fork 705
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
Optimise Apply[{ Either, \/ }].apply2 #1958
Optimise Apply[{ Either, \/ }].apply2 #1958
Conversation
@fommil Awesome work. Would be great to do this for |
@jdegoes done, that actually has a non-negligible impact on my perf tests which aren't even testing for this!
|
https://typelevel.org/blog/2014/11/10/why_is_adt_pattern_matching_allowed.html#adts-are-ok-to-go suggests that these optimisations are unneeded. My perf tests suggest otherwise on 2.12... |
You have quite a few "optimizations" here, not just touching |
@edmundnoble the numbers cited are just the |
case \/-(a) => \/-(g(a)) | ||
case b @ -\/(_) => b | ||
case \/-(b) => \/-(g(b)) | ||
case a => a.asInstanceOf[A \/ D] |
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.
faster because there is no check in the second case
@xuwei-k what do you think? |
def traverseDisjunction[E, B](f: A => E \/ B): E \/ IList[B] = \/-( | ||
map { a => | ||
f(a) match { | ||
case -\/(err) => return -\/(err) |
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.
write a tailrec function instead of using exceptions?
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.
Also map
runs in the opposite direction:
def map[B](f: A => B): IList[B] = reverse.reverseMap(f)
It starts from the end, so your traverse
is not the same as the usual one.
^^ a perfect example why side-effects are evil ;)
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.
def traverse[E, A, B](l: List[A], f: A => E Either B): E Either List[B] = {
@annotation.tailrec def go(l: List[A], r: List[B]): E Either List[B] =
l match {
case a :: as =>
f(a) match {
case Right(b) => go(as, b :: r)
case Left(e) => Left(e)
}
case Nil => Right(r.reverse)
}
go(l, Nil)
}
println(
traverse(
List(1, 2, 3, 4),
(a : Int) => if (a % 2 == 0) Left(a) else Right(a)))
close #1956
Running this perf test
from https://gitlab.com/fommil/scalaz-deriving
BEFORE this change
AFTER this change
and WITH custom short circution impl of traverseDisjunction from #1956 (which is used in the repo's master... back out that change to run the above tests)
async-profiler alloc tracing shows that the
\/-
allocations are quite heavy. I guess that's to be expected.