Motivation
For motivation see: PRE SIP: ThisFunction | scope injection (similar to kotlin receiver function)
Proposal
I propose that we can annotate an argument with the keywords import
/ export
to cause an implied import/export of the annotated argument’s namespace inside the body of the method/class (arguments can be given or explicit).
Classes Interaction
The following code will be simply rewritten.
Before Rewrite
has import/export arguments
class Scope {
val a : Int = 0
}
class ExtraScope extends Scope {
val b : Int = 0
}
class Importer[S <: Scope](given import scope : S) {
val aAccess = a
}
class Exporter[S <: Scope](given export scope : S) {
val aAccess = a
}
given ExtraScope
val im = new Importer[ExtraScope] {
val bAccess = b
}
val ex = new Exporter[ExtraScope] {
val bAccess = b
}
val aAccess = ex.a
val bAccess = ex.b
After Rewrite
import/export arguments changed to import/export statements
class Scope {
val a : Int = 0
}
class ExtraScope extends Scope {
val b : Int = 0
}
class Importer[S <: Scope](given val scope : S) {
import scope._
val aAccess = a
}
class Exporter[S <: Scope](given val scope : S) {
export scope._
val aAccess = a
}
given ExtraScope
val im = new Importer[ExtraScope] {
import scope._
val bAccess = b
}
val ex = new Exporter[ExtraScope] {
export scope._
val bAccess = b
}
val aAccess = ex.a
val bAccess = ex.b
Import Function Arguments
import
function arguments are special-cased and are treated differently than value arguments. The import statement will take effect inside
Before Rewrite
class Scope {
val a : Int = 0
}
class ExtraScope extends Scope {
val b : Int = 0
}
def importer[S <: Scope, T](import block : S => T)(given s : S) : T = block(s)
given ExtraScope
val bAccess = importer{s : ExtraScope =>
val aAccess = a
b
}
After Rewrite
class Scope {
val a : Int = 0
}
class ExtraScope extends Scope {
val b : Int = 0
}
def importer[S <: Scope, T](block : S => T)(given s : S) : T = block(s)
given ExtraScope
val bAccess = importer{s : ExtraScope =>
import s._
val aAccess = a
b
}
Rules
- import/export arguments can be anonymous. This helps us avoid polluting the namespace with the owner of the scope.
class Importer[S <: Scope](given import S) {
val aAccess = a
}
class Foo(import Scope)
- import/export value arguments are automatically inferred to be public values (not function arguments).
- defs do not allow export arguments (it doesn’t really matter, but I don’t think we should allow both keywords to do the same thing in defs).
- import/export annotations are not allowed for by-name values.
- import/export function arguments can be high-order functions. All their arguments will be imported.