is better than
because it doesn’t require parentheses around Math.sqrt
is better than
because it doesn’t require parentheses around Math.sqrt
a.zip(b).map(_ * _).sum.|>(Math.sqrt)
Scala 2 is agnostic about the invocation style (infix vs dotted selection).
Scala 3 wants only symbolic operators for infix, though you can backtick an alnum:
def f = s `pipe` len // from my previous comment
def check(x: AnyRef, y: AnyRef) = x.eq(y) // we used to write (x eq y)
It turns out that the old style of omitting dots and parentheses failed to avert global warming and shrink the Great Pacific Garbage Patch.
Since I tend to need them for ease of reading, I use as many as necessary, guilt-free.
But symbolic operators can be tastefully applied. The discussion was about how to indicate that “pipe” is a synonym, in order to make it easier to search documentation. I don’t remember if they invented a mechanism for that. Maybe the alpha annotation.
Maybe it would be great to return /:
and :/
back to Scala? These operators was really great
I see! I didn’t know about the infix notation. Now that I do, here’s another attempt at the distance function:
(a zip b).map(_ * _).sum pipe Math.sqrt
In this version, I love the infix zip
, but I’m a little iffy about the infix pipe
because I don’t really think of pipe
as an infix operator. In my mind, every time I see pipe
used over and over with the same function, that’s an indicator that this function should really be made available as a method. For example:
(a zip b).map(_ * _).sum.sqrt
Or even
(a zip b).map(_ * _).sum.(Math.sqrt)
As you can see, .
and .pipe
mean the same thing in this situation. Would it be possible to overload .
to behave like .pipe
in situations like this? Or would that overload .
too much?
That seems like it would be really easy to mix up with the sugar for .apply
By coincidence, I read the PR thread on “implicit application syntax” for Dotty from 2017-18 or so, and someone did propose dot application. I wish I could find it amongst those long, long threads. I just wanted to point out that it came up at least once, in a certain context.
Edit: dot application
implicit val str: String = "x = "
def foo(x: Int).(y: String) = y+x
foo(0) // "x = 0"
foo(1).("Value: ") // "Value: 1"
Thank you to everyone who responded so far. I feel like I need to come back to this discussion after learning more Scala!
If you’re interested, here’s the “opposite” discussion on the F# github about adding the fluent .
methods to FSharp Core. Consider incorporating FSharp.Core.Fluent into FSharp.Core · Issue #1073 · fsharp/fslang-suggestions · GitHub. The majority of votes are against the proposal.
I love piping partial functions all day long, and I think I’ll find out soon enough how often I need to use .pipe
to do so. But adding an operator, even a common one like |>
, is a slippery slope. If Scala adds |>
, it will probably open the floodgates for people asking for more symbolic operators for compose
, andThen
, etc. And it will certainly open the floodgates for people writing functions where the main argument comes last, not first. All of which detract from the Subject Verb Object essence of Scala? (Fresh eyes here–not sure if this is actually the essence of Scala )
I think .pipe
adds a little bit of friction and reminds library authors that super common functions should be exposed as methods for easier chaining.
This happened already in the past. Scala was for some time considered the Perl of JVM languages, written in the most obscure symbolic operators.
Thanks god most of the proponents are back to Haskell (or whatever other language without IDE friendly syntax).
Even the Haskell clone libs in Scala don’t write code backwards anymore but use method syntax.
Yikes, I just realized that I got the distance formula wrong! The correct formula is:
(a zip b).map(_ - _).map(x => x * x).sum.pipe(Math.sqrt)
Roc-lang use |>
too
If Scala adds
|>
, it will probably open the floodgates for people asking for more symbolic operators forcompose
,andThen
, etc.
An excerpt from Sergei Winitzki’s book: https://github.com/winitzki/sofp:
Here you see the difficulty with choosing method names. f andThen g
is completely self explanatory, f
will be applied first and then comes g
. But f compose g
?? This can be either way. After many years of Scala programming i still have to look it up to be sure. f butFirst g
would have been a lot clearer for example, and it is also obvious that andThen
and butFirst
are antagonists. BTW, before
and after
would have been perfect as well. Equally clear and simpler.
Let me not start on those operators, great for personal libraries, but not for the standard.
Agreed that compose
is not a very intuitive name, and I don’t think I ever use that operator. But there is some logic behind the name. It is borrowed from mathematical function composition.
Quoting wikipedia:
In mathematics, function composition is an operation ∘ that takes two functions f and g, and produces a function h = g ∘ f such that h(x) = g(f(x)).
I might have gone with f after g
.
I do like map
and contramap
, which you can kind of get to with Cats, though the type inferencing doesn’t quite seem to be able to make the connection (which shouldn’t be an issue if implemented as a member, rather than through a typeclass).
import cats.syntax.all._
import cats.Contravariant
val f: Float => Int = ???
val g: Int => String = ???
val fg: Float => String = f.map(g)
val gf: Float => String = Contravariant[* => String].contramap(g)(f)
// Sadly, this doesn't work:
g.contramap(f)
// It's equivalent to this, which does work:
toContravariantOps[* => String, Int](g)(cats.Invariant.catsContravariantForFunction1).contramap(f)
Even Bartosz Milewski says the following:
In math, such composition is denoted by a small circle between functions: g∘f. Notice the right to left order of composition. For some people this is confusing.