Discussion:
[scala-language] Allow top-level "implicit class"es by folding them into containing package-object
Ryan Williams
2016-12-07 03:26:24 UTC
Permalink
The requirement that implicit classes be explicitly declared inside another
object gets in my way frequently and feels like it could be relaxed.

My typical library code looks like:

package foo.bar

object Baz {
implicit class Baz(
) { 
 }
}

and corresponding calling code:

import foo.bar.Baz.Baz

One of the two Baz-layers is redundant.

Logically, i want my implicit classes to be considered to live inside their
nearest containing package object (in this case foo.bar), just like
non-implicit classes, but I can only declare a given package object once,
so to use it I have to put all classes in a package in the same file (in
the package object), which is also suboptimal.

LMK if I'm missing something? Thanks!
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Jason Zaugg
2016-12-07 04:28:55 UTC
Permalink
Unfortunately we have a number of assumptions in our toolchain (mostly
driven by the requirement of separate+incremental compilation), that each
class file comes from a single source file. Package objects are modelled as
an object with the name package, so like any other object they must be
defined within a single source file (by convention, named package.scala.)


I believe instead that we should allow top level implicit classes and treat
it as though it introduced companion object of type Function1[Wrapped,
Wrapper]. The companion itself would be seen as an implicit. The call to
Wrapper.apply(wrapped) couId be translated to new Wrapper(wrapped) in order
to make later optimizations for implicit classes work as expected. We
already do this for calls to the companion apply methods of case classes.
​

-jason
Post by Ryan Williams
The requirement that implicit classes be explicitly declared inside
another object gets in my way frequently and feels like it could be
relaxed.
package foo.bar
object Baz {
implicit class Baz(
) { 
 }
}
import foo.bar.Baz.Baz
One of the two Baz-layers is redundant.
Logically, i want my implicit classes to be considered to live inside
their nearest containing package object (in this case foo.bar), just like
non-implicit classes, but I can only declare a given package object once,
so to use it I have to put all classes in a package in the same file (in
the package object), which is also suboptimal.
LMK if I'm missing something? Thanks!
--
You received this message because you are subscribed to the Google Groups
"scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
martin odersky
2016-12-07 08:51:09 UTC
Permalink
Post by Jason Zaugg
Unfortunately we have a number of assumptions in our toolchain (mostly
driven by the requirement of separate+incremental compilation), that each
class file comes from a single source file. Package objects are modelled as
an object with the name package, so like any other object they must be
defined within a single source file (by convention, named package.scala.)
I believe instead that we should allow top level implicit classes and
treat it as though it introduced companion object of type Function1[Wrapped,
Wrapper]. The companion itself would be seen as an implicit. The call to
Wrapper.apply(wrapped) couId be translated to new Wrapper(wrapped) in
order to make later optimizations for implicit classes work as expected. We
already do this for calls to the companion apply methods of case classes.
But there's another problem: How do you figure out that a top-level class
is implicit? The compiler has only the name of the file as an entry in the
enclosing package scope. It would have to look inside the file to find out
whether it contained an implicit class. That would break all our
assumptions about separate compilation.

- Martin
Post by Jason Zaugg
Post by Ryan Williams
The requirement that implicit classes be explicitly declared inside
another object gets in my way frequently and feels like it could be
relaxed.
package foo.bar
object Baz {
implicit class Baz(
) { 
 }
}
import foo.bar.Baz.Baz
One of the two Baz-layers is redundant.
Logically, i want my implicit classes to be considered to live inside
their nearest containing package object (in this case foo.bar), just
like non-implicit classes, but I can only declare a given package object
once, so to use it I have to put all classes in a package in the same file
(in the package object), which is also suboptimal.
LMK if I'm missing something? Thanks!
--
You received this message because you are subscribed to the Google Groups
"scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
Martin Odersky
EPFL and Lightbend
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Roland Kuhn
2016-12-07 09:04:46 UTC
Permalink
Unfortunately we have a number of assumptions in our toolchain (mostly driven by the requirement of separate+incremental compilation), that each class file comes from a single source file. Package objects are modelled as an object with the name package, so like any other object they must be defined within a single source file (by convention, named package.scala.)
I believe instead that we should allow top level implicit classes and treat it as though it introduced companion object of type Function1[Wrapped, Wrapper]. The companion itself would be seen as an implicit. The call to Wrapper.apply(wrapped) couId be translated to new Wrapper(wrapped) in order to make later optimizations for implicit classes work as expected. We already do this for calls to the companion apply methods of case classes.
But there's another problem: How do you figure out that a top-level class is implicit? The compiler has only the name of the file as an entry in the enclosing package scope. It would have to look inside the file to find out whether it contained an implicit class. That would break all our assumptions about separate compilation.
Would it be thinkable to forego separate compilation? Source files whose translation has not been invalidated since the last run could just be rehydrated from their class files (assuming that this can be made quick enough) or even kept in memory between runs. How close is this to what zinc does?

Regards,

Roland
- Martin
The requirement that implicit classes be explicitly declared inside another object gets in my way frequently and feels like it could be relaxed.
package foo.bar
object Baz {
implicit class Baz(
) { 
 }
}
import foo.bar.Baz.Baz
One of the two Baz-layers is redundant.
Logically, i want my implicit classes to be considered to live inside their nearest containing package object (in this case foo.bar), just like non-implicit classes, but I can only declare a given package object once, so to use it I have to put all classes in a package in the same file (in the package object), which is also suboptimal.
LMK if I'm missing something? Thanks!
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
For more options, visit https://groups.google.com/d/optout <https://groups.google.com/d/optout>.
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
For more options, visit https://groups.google.com/d/optout <https://groups.google.com/d/optout>.
--
Martin Odersky
EPFL and Lightbend
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
For more options, visit https://groups.google.com/d/optout <https://groups.google.com/d/optout>.
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Jason Zaugg
2016-12-07 10:31:42 UTC
Permalink
Post by Jason Zaugg
Unfortunately we have a number of assumptions in our toolchain (mostly
driven by the requirement of separate+incremental compilation), that each
class file comes from a single source file. Package objects are modelled as
an object with the name package, so like any other object they must be
defined within a single source file (by convention, named package.scala.)
I believe instead that we should allow top level implicit classes and
treat it as though it introduced companion object of type Function1[Wrapped,
Wrapper]. The companion itself would be seen as an implicit. The call to
Wrapper.apply(wrapped) couId be translated to new Wrapper(wrapped) in
order to make later optimizations for implicit classes work as expected. We
already do this for calls to the companion apply methods of case classes.
But there's another problem: How do you figure out that a top-level class
is implicit? The compiler has only the name of the file as an entry in the
enclosing package scope. It would have to look inside the file to find out
whether it contained an implicit class. That would break all our
assumptions about separate compilation.
Good point. To fit in with that constraint, we'd need some scheme to encode
the implicit modifier into the name of a some class. For instance, we could
do something like:

implicit class RichString(val s: String) {
/*synthetic*/ class Implicit$ // added by the compiler
}

​
There is precedent for adding synthetic inner classes (e.g javac does to
add to the signatures of "access constructors":
https://gist.github.com/retronym/8ca8026f5b7eb981b53f5c8923d50f54)

-jason
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
martin odersky
2016-12-07 10:35:15 UTC
Permalink
Post by Jason Zaugg
Post by Jason Zaugg
Unfortunately we have a number of assumptions in our toolchain (mostly
driven by the requirement of separate+incremental compilation), that each
class file comes from a single source file. Package objects are modelled as
an object with the name package, so like any other object they must be
defined within a single source file (by convention, named package.scala.)
I believe instead that we should allow top level implicit classes and
treat it as though it introduced companion object of type Function1[Wrapped,
Wrapper]. The companion itself would be seen as an implicit. The call to
Wrapper.apply(wrapped) couId be translated to new Wrapper(wrapped) in
order to make later optimizations for implicit classes work as expected. We
already do this for calls to the companion apply methods of case classes.
But there's another problem: How do you figure out that a top-level class
is implicit? The compiler has only the name of the file as an entry in the
enclosing package scope. It would have to look inside the file to find out
whether it contained an implicit class. That would break all our
assumptions about separate compilation.
Good point. To fit in with that constraint, we'd need some scheme to
encode the implicit modifier into the name of a some class. For instance,
implicit class RichString(val s: String) {
/*synthetic*/ class Implicit$ // added by the compiler
}
​
There is precedent for adding synthetic inner classes (e.g javac does to
add to the signatures of "access constructors": https://gist.
github.com/retronym/8ca8026f5b7eb981b53f5c8923d50f54)
-jason
--
You received this message because you are subscribed to the Google Groups
"scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
That's an interesting idea! I had not thought of that. That could indeed
solve the problem.

- Martin
--
Martin Odersky
EPFL and Lightbend
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...