Skip to content

File-level scopes #209

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
altavir opened this issue Mar 25, 2020 · 0 comments
Open

File-level scopes #209

altavir opened this issue Mar 25, 2020 · 0 comments

Comments

@altavir
Copy link

altavir commented Mar 25, 2020

The proposal I am going to write here concerns several KEEPs, so I am writing it as an issue. Specifically I want to touch former KEEP-75 (scripting support), KEEP-87 and KEEP-176.

Problems

There are several problems, I want to address:

  • Scripting. Script can use an implicit receiver parameter, meaning it is called in some specific context. The problem is that the context is not present unless the script is called, so it is not possible to use static checks and auto-completion on such script. The problem is discussed here. In order to fix that we need a way to let the script know, from where it will be called and do not allow it to be called outside of that context.

  • Type-classes. The major controversial point of KEEP-87 is the resolution strategy for extension interfaces. It is important to have explicit way to declare implementation strategy for an extension interface (type-class).

  • Multiple receivers. The main problem is the conflict between KEEP-176 and KEEP-87. They cover mostly the same cases, but are slightly different. It would be nice to have both features sets in the same package.

Solution

Let us assume that it is possible to declare file-level receivers like it written here. The proposal is to declare an interface that a receiver will adhere to. It could look like this (I do not propose this specific syntax):

interface MyInteface{
  fun String.doSomething()
  fun String.doOtherSomething()
}
import ...
use com.mypackage.MyInterface

fun callFunction(){
  "fff".doSomething()
}

When this hypothetical use directive is used, all functions and classes inside the file are assumed to be run in the close in the scope of MyInterface as though they are run with implicit receiver.

When we call those functions from outside code, those values, functions and class constructors could be called only inside the scope, produced by MyInterface. like that:

callFunction() // compile-time error

val scope: MyScope = object: MyScope{
  override fun String.doSomething() = println("doSomething")
  override fun String.doOtherSomething() = println("doOtherSomething")
}

with(scope){
  callFunction()  // works
}

Here scope could be not only a stateless object, but also a state-full receiver, which is important for example in scripting.

UPDATE: An important note. If the place, file members are called also have file-level scope, it is obviously automatically passed, so no need to pass it explicitly. So depending on initial call place, one could have different implementation.

Also in order to avoid explicit definition clutter, criticized by significant part of community, we can explicitly bind an object as a file-level receiver like that:

object MyObject: MyInterface{
...
}

use com.mypackage.MyObject for com.mypackage.MyInterface
//or simply
use com.mypackage.MyObject //interface could be omitted since it declares an interface of its own

In this case an explicit receiver in external code is not needed and we get a functionality of type-classes.

The proposal, of course would not be fully functional without multiple receivers (KEEP-176), but probably could be partially done using existing scripting mechanics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant