-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Adds tap and pipe methods (available via import scala.util.chaining._)
#7007
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
Conversation
Fixes scala/bug#5324 This implements an opt-in enrichment for any type called tap and pipe: ```scala scala> import scala.util.chainingOps._ import scala.util.chainingOps._ scala> val xs = List(1, 2, 3).tap(ys => println("debug " + ys.toString)) debug List(1, 2, 3) xs: List[Int] = List(1, 2, 3) scala> val times6 = (_: Int) * 6 times6: Int => Int = $$Lambda$1727/1479800269@10fbbdb scala> (1 + 2 + 3).pipe(times6) res0: Int = 36 scala> (1 - 2 - 3).pipe(times6).pipe(scala.math.abs) res1: Int = 24 ```
dwijnand
left a comment
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 Eugene.
|
Sorry that this is a bikeshedding comment but it may be nicer to just import import scala.collection.JavaConverters._
List(1).asJava
import scala.concurrent.duration._
1.second
import scala.util.chaining._
1.pipe(scala.math.abs) |
|
Can we get a performance warning on these? Something like, and then add inlining instructions to fix the overhead (if inlining will actually fix it)? I continue to think it is a bad idea to include library features with the semantics of, say, |
In the Scaladoc, sure. Or in a linting rule included in a third-party performance-oriented linting ruleset, sure. As a compiler warning, I feel strongly that no, that really wouldn't be appropriate. I think both Scala itself and the Scala standard library are already teeming with low-level performance gotchas like this — an extra method call, non-obvious boxing, allocating an extra short-lived object, that kind of thing. It's par for the course, not something the compiler constantly needs to call out. |
|
I agree with @joshlemer's naming suggestion, any other opinions on that? |
|
@SethTisue - I just mean in the scaladoc! As you say, there are lots of performance gotchas already, and many are worse than these. Still, it's nice to know when two semantically identical things have significantly different performance characteristics. (This isn't even specialized, so it boxes primitives too!) |
|
I also agree regarding naming the import |
LOL, sorry for the overreaction then :-) |
|
I should be more careful when using the term "warning" since it has a technical meaning when it applies to compiler messages. |
|
Is there a reason not to make this specialized? (At least as specialized as Function1 is?) |
|
Looks like all the feedback has been addressed. |
| * @return a new value resulting from applying the given function | ||
| * `f` to this value. | ||
| */ | ||
| def pipe[B](f: A => B): B = f(self) |
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.
Other languages name this |>. Would be great to do so here too.
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.
Mentioned here: #6767 (comment)
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 reasoning is that symbolic operators are bad. Though, as a small datapoint: I have no idea what <<=, <+= or <++= mean, I only understand >>= if you tell me its name, I always mix up /: and :\, but |> is just obvious to me, and is better at visually separating steps than pipe. And you have to explicitly import this anyway so having this symbolic alias wouldn't be the worst thing.
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.
Ideally if we can ignore backward compat is get rid of match as keyword and make it a function named match. |> is basically match.
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.
Please add |> too.
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.
Seconding @japgolly . I'm not for symbols in general but this is a very very special ubiquitous one. bash uses something similar, F# has it, PowerShell too.
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.
@ScalaWilliam You can add OCaml & ReasonML to that list.
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.
Maybe one of you should send a new pull request or contributor thread and get consensus around it.
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.
|
According to some quick tests at least the implicit conversion should definitely be made |
|
🤔 resolvers += "scala-pr" at "https://scala-ci.typesafe.com/artifactory/scala-integration/"
scalaVersion := "2.13.0-pre-79d5047" |
|
compiler bug? (regression?) 🤔 |
scala/bug#10768 ? or another issue?🤔 |
|
paging @milessabin in aisle literal types |
Hmmm, maybe another issue 🤔? |
|
I'll take a look. |
|
Looks like a variant of scala/bug#10792 (which was fixed in #6454). |
My example ( 79d5047 ) contains #6454 commits but still fail. |
|
Yeah, it's a variant that's not covered by that fix. |
|
Re familiarity of |
|
For anyone doubting that which could have a substantial performance impact on small byte arrays. Maybe someone wants to benchmark it both ways; it's more hassle to benchmark the difference than it's worth to avoid writing it a way that's assuredly fast. (One could counter: but |
|
I've just fixed the issue reported by @xuwei-k. With #7070 the result type of his example is now revealed to be a bit of a monstrosity, scala> case class Foo[A](self: A) { def bar: self.type = self }
defined class Foo
scala> val res = Foo(1).bar
^
warning: inferred existential type _1.self.type forSome { val _1: Foo[Int] }, which cannot be expressed by wildcards, should be enabled
by making the implicit value scala.language.existentials visible.
----
This can be achieved by adding the import clause 'import scala.language.existentials'
or by setting the compiler option -language:existentials.
See the Scaladoc for value scala.language.existentials for a discussion
why the feature should be explicitly enabled.
res: _1.self.type forSome { val _1: Foo[Int] } = 1
|
import scala.util.chaining._)
|
renewed discussion at #7326 of whether to have |
This implements opt-in extension methods, on all types, called
tapandpipe:Fixes scala/bug#5324
This is a resend of #6767 without the macro commits.