For a while now I’ve been trying to gather my thoughts on this subject so here goes.
I’d like to propose hopefully and improvement to just the new syntax for defining extension methods
.
My main goal is to try improving the learnability and second readability of extension methods
.
Extension methods
extend String with {
def extMethod = this + ".ext"
}
// using the extension method
assert("abc".extMethod == "abc.ext")
This is the most basic example I could think of.
In my eyes, this code requires no explanation.
It builds on the knowledge/expectations of how class/trait inheritance works.
Such a simple example but it impacts quite substantional
- build on what you already know (how to define a class)
- it makes the code much more readable and learnable
- when defining more than one extension method it gets rid of a lot of duplication/noise
- having the extension methods grouped together will help maintainability
// current syntax
// i am sorry but my eyes can't parse this,
// and it's now obvious what to expect when you're seeing this for the first N times
// this syntax is new to everyone, current, and future scala developers
def (s: String) method1 (arg: Int) = ???
def (s: String) method2 (arg: Int) = ???
def (s: String) method3 (arg: Int) = ???
// vs
extend String with {
def method1(arg: Int) = ???
def method2(arg: Int) = ???
def method2(arg: Int) = ???
}
Type-classes
The new proposed syntax would also impact/change the type-class definition, also in a positive way (at least in my eyes), by making the extension methods explicit.
Type-classes example:
trait Ord[T] {
def compare(t1: T, t2: T): Int
extend T with {
def <(t2: T) = compare(this, t2) < 0
def >(t2: T) = compare(this, t2) > 0
}
}
This was just a very basic example of a type-class definition.
- it reads quite well for java/kotlin developers.
- it build on top of already know topics inheritance/class definition.
- learning is easier since the
extend
methods are a separate subject to learn. - it makes the
extension methods
explicit and because of this, you know what behavior to expect.
Now try to look at the above example again, but this time try reading it as if you don’t know the concept of what a type-class is.
Me reading it would be something like:
Ord is just a trait (i know what a trait is).
It has just one methodcompare
ok.
This trait also extends the type T with additional extension methods
So if I know about traits and type parameters/generics.
Wow type-classes are easy.
They are just parameterized traits with extension methods … sounds like Adhoc polymorphism
Optional naming and imports
The proposed changes should only be syntactical, so the Extension Methods would apply
Additionally, we could provide a name like
package org.example
extend String with OptionalName {
def method1 = ???
}
Complete example:
trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A=>B): F[B]
extend F with {
def map(f: A=>B): F[B] = map(this)(f)
}
}
trait Monad[F[_]] extends Functor[F] {
def flatMap[A,B](fa:F[A])(f: A=>F[B]): F[B]
def map[A,B](fa: F[A])(f: A=>B): F[B] = flatMap(fa)(f `andThen` pure)
def pure[A](a: A): F[A]
extend F with {
@alpha("bind")
def >>=(f: A=>F[B]) = flatMap(this)(f)
}
}
given listMonad: Monad[List] {
def flatMap[A,B](xs: List[A])(f:A=>List[B]): List[B] = xs.flatMap(f)
def pure[A](a: A): List[A] = List(a)
}