PRE SIP: ThisFunction | scope injection (similar to kotlin receiver function)

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 orphan Monoid[Int]. Suddenly all of your code that relied on automatically imported orphan Monoid[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 vals by default - you have to add explicit var if you want mutability
  • methods and function parameters (and also intermediate values in for-comprehensions) are vals and you can’t change them at all - you need to copy them to some other vars 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 a val
  • 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 use scala.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 :slight_smile: 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)
4 Likes