Having Another Go at Exports (Pre-SIP)

You could allow inline export syntax to opt into the inline semantics.

1 Like

Is it possible to allow exporting members in self-type?

type ID = Any { self: Long =>
  export self.{+, -}
}

I think it would be useful when creating opacity types Proposal for Opaque Type Aliases

1 Like

That’s not legal synax, but let’s assume instead this:

trait ID { self: Long =>
  export self.{+, -} 
}

You can write that, but you won’t be able to tie the knot afterwards. + and - would be final in ID. At some point you have to mix Long and ID in one class. At that point you would get an “cannot override final member” error.

It’s not legal syntax, it’s syntax he proposed in the other thread

If we allow self-types for type aliases, there would not be the mix type problem, since the typer knows that ID =:= Long at the context inside ID .

If my library uses export from another library, e.g., shapeless, at top level, does it mean that the user of my library will get all of the shapeless namespace?

I’m unsure what the rules for implicit values are.

trait Foo
object Import {
  implicit val foo : Foo = new Foo{}
}
object Export {
  implicit val foo : Foo = new Foo{}
}

import Import._
export Export._
implcitly[Foo]

Do we have a name collision? Do we have an ambiguity? Does the order between import and export matter?

Just for comparison:
https://kotlinlang.org/docs/reference/delegation.html

interface Base {
    fun printMessage()
    fun printMessageLine()
}

class BaseImpl(val x: Int) : Base {
    override fun printMessage() { print(x) }
    override fun printMessageLine() { println(x) }
}

class Derived(b: Base) : Base by b {
    override fun printMessage() { print("abc") }
}

I think it is just a simple.

It’s indeed just as simple in this case. But what if the exported object is not an argument but a locally defined member? I don’t see how this would work with by, so it seems that this construct does not support aggregation in general.

I just wanted to say that overriding is more simple in kotlin.

The kotlin’s documentaion says:
The Delegation pattern has proven to be a good alternative to implementation inheritance

And we have such relatively complicated rule for overriding.

But such overriding is more complicated in general.

Let’s look, how this can be applied to the problem of the composability of the import
statements (where you want to have a way to ‘shrink’ few import statements into one, like importing db access API with a specific context, as in motivation example in https://docs.google.com/document/d/1dlT6NgB9610jqLscCJW2LRB7TapDh3q4d2S3YA_q5zs/edit?usp=sharing )

The simplest possible analog version in dotty will-be:

DAO:

package db

class  DAO[K,T]  given PersistencyOptions[T], IdOptions[K,T]  {
     def  find(id: k): Future[Option[T]]
     def  save(value:T):  Future[Unit]
}

MyProjectDBImplicits:

package myproject.dboptions

implied def myPersistenceOptions[T]  given Generic[T]

Also exists module for default db.stdoptions

package db.stdoptions

implied def defaultPersistenceOptions[T]  given Generic[T]

So, I want to have one clause, i.e. I want:

object MyCompanyDAOS  {
     export db._
     implied export myproject.dboptions._
}

But for using this, developers should write:

import MyCompanyDAOS._
implied import MyCompanyDAOS._

(i.e. still two imports, and developers also have the possibility to shoot yourself in the foot, by writing

import MyCompanyDAOS._
implied import db.stdoptions._

)

So, the original problem still exists, but now not because of imports uncomposability, but because we require separate imports for ‘normal’ and ‘implicit’ cases.

The other part (ie. shrink set of different implicit-s into one) this PRE-SIP cover well. So, we can say that 1/2 of the original problem is solved :wink:

A question which remains open for me: is it possible to restrict use of required implicit values, in such way, that

import MyCompanyDAOS._
implied import db.stdoptions._

should generate compile-time error ?

Having old-style ‘full’ imports, with ‘explicit’ and ‘implicit’ as options, as proposed in the Import implied covering import will fix this issue.

Instead of omitting existing names from wildcard exports you could make the exported methods non-final to enable delegation. This is what we typically do anyway for big interfaces. See https://github.com/slick/slick/blob/master/slick-testkit/src/main/scala/com/typesafe/slick/testkit/util/DelegateConnection.scala for an example.

You don’t implement forwarders for missing methods in all possible Connection impementations that delegate the other methods to another object. Instead you implement a DelegateConnection which delegates all calls once and then subclass it to override individual methods.

This is not as convenient for ad-hoc delegation because you need an additional delegate class but it pays off as soon as you need more than one implementation.

I believe that would make export a powerful (and rather dangerous!) booster for inheritance based architectures. But that’s not its purpose.

It’s purpose is to provide something simpler where inheritance is not necessary. I believe final is an essential ingredient for that. It prevents you from playing overriding tricks with export aliases, and I believe that’s a good thing.

1 Like