Requiring import is not excluding. Orphan instances require import for sanity. Otherwise:
- there would be compilation performance penalty not only during error reporting with import suggestion, but also during normal compilation passes (automatic orphan imports require scanning the whole classpath)
- it would be very easy to have ambiguities. Let’s say you had only one
Monoid[Int]
on classpath and were happy with automatically imported orphan instances. Then you add some library to you app and that library brings another orphanMonoid[Int]
. Suddenly all of your code that relied on automatically imported orphanMonoid[Int]
instance breaks because of ambiguity.
Going back to receiver methods:
My stance is that Scala should encourage pure code over side-effecting code. Receiver functions are practically fully mutability oriented, i.e. all examples of receiver functions usage revolved around mutable builders or some other ugly imperative Javaism like that (and I haven’t switched from Java to Scala only to see more ugly imperative Javaisms).
Let’s also quote Kotlin docs https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver to see how they work:
Therefore the syntax that I’ve proposed before:
receiver.function { this =>
... here we have new 'this'
}
closely matches what Kotlin’s receiver functions do.
This syntax introduces small penalty for receiver functions, makes them perfectly comprehensible and also allows users to opt-in or opt-out whenerver they want at use-site (receiver functions from Kotlin don’t have that flexibility).
Scala has penalties for mutability oriented code in other places, e.g.:
- case class primary constructor parameters are
val
s by default - you have to add explicitvar
if you want mutability - methods and function parameters (and also intermediate values in for-comprehensions) are
val
s and you can’t change them at all - you need to copy them to some othervar
s explicitly - default collections available without prefix are (almost?) all immutable ones - you need to explicitly import the mutable ones
- you can’t import from a
var
but you can import from aval
- there’s no
continue
keyword,return
works often by throwin exceptions (so it breaks in async code then),break
is absent and you need to usescala.util.control.Breaks
(which I never seen used) - etc there are plenty of such examples
- therefore if you’re after mutability oriented code then you’ll want to avoid Scala anyway and Scala wants to avoid you
Mutability restrictions in Scala are not as tough as in Haskell (which outright rejects all mutable code no wrapped in IO type), but still Haskell is a strong inspiration (see scalaz, cats, etc)