Vagif Abilov Vagif Abilov - 11 days ago 4x
Scala Question

Why such operator definition is possible in Scala?

I use F# and don't know much of Scala, except that there are often some similarities between these languages. But while looking at Akka Streams implementation in Scala, I noticed the use of operator ~> in such a way that's not possible in F# (unfortunately). I am not talking about symbol "~" that can only be used in F# in the beginning of unary operators, this is not important. What impressed me is possibility to define graphs like this:

in ~> f1 ~> bcast ~> f2 ~> merge ~> f3 ~> out
bcast ~> f4 ~> merge

Since various graph elements have different types (Source, Flow, Sink), it's not possible to define a single operator in F# that would work across them. But I wonder why this is possible in Scala - it this because Scala supports method overloading (and F# doesn't)?


Actually, Scala has at least four different ways to make it work.

(1) Method overloading.

def ~>(f: Flow) = ???
def ~>(s: Sink) = ???

(2) Inheritance.

trait Streamable { 
  def ~>(s: Streamable) = ???
class Flow extends Streamable { ... }
class Sink extends Streamable { ... }

(3) Typeclasses and similar generic constructs.

def ~>[A: Streamable](a: A) = ???

(with Streamable[Flow], Streamable[Sink], ... instances that provide the needed functionality).

(4) Implicit conversions.

def ~>(s: Streamable) = ???

(with implicit def flowCanStream(f: Flow): Streamable = ???, etc.).

Each of these have their own strengths and weaknesses, and all are used heavily in various libraries, though the last has fallen somewhat out of favor due to it being too easy to generate surprises. But to have the behavior you have described, any of these would work.

In practice, in Akka Streams, it's actually a mixture of 1-3 from what I can tell.