Given an extension method defined using the implicit class syntax:
package casa
object MyExtensions:
implicit class IntOps(val underlying: Int):
def twice: Int = underlying * 2
implicit class StringOps(val underlying: String):
[...]
I can explicitly import it and use it with
#main def double(): Unit = {
import casa.MyExtensions.IntOps
println(s"2 * 2 = ${2.twice}")
}
However when I use Scala 3's extension syntax:
package casa
object MyExtensions:
extension (underlying: Int)
def twice: Int = underlying * 2
extension (underlying: String)
[...]
I seem to have to wildcard-import the extension:
#main def double(): Unit = {
import casa.MyExtensions.*
println(s"2 * 2 = ${2.twice}")
}
Is there a way to explicitly import only a specific extension?
Related
In the following example, I want to use tagged types for the ids of my classes. I've created an utility trait to reduce some boilerplate (of tags/reads/writes declaration):
import java.util.UUID
import play.api.libs.json.{Format, Json, Reads, Writes}
trait Opaque[A] {
protected type Tagged[U] = { type Tag = U }
type ##[U, T] = U with Tagged[T]
trait Tag
def tag(a: A): A ## Tag = a.asInstanceOf[A ## Tag]
def untag(a: A ## Tag): A = a
implicit def reads(implicit r: Reads[A]): Reads[A ## Tag] =
r.map(tag)
implicit def writes(implicit w: Writes[A]): Writes[A ## Tag] =
w.contramap(untag)
implicit def format(implicit r: Reads[A], w: Writes[A]): Format[A ## Tag] =
Format(reads(r), writes(w))
}
final case class Foo(id: Foo.FooId.T, f1: Boolean)
object Foo {
object FooId extends Opaque[UUID] {
type T = UUID ## Tag
}
import FooId._
implicit val fmt: Format[Foo] = Json.format[Foo]
}
final case class Bar(id: Bar.BarId.T, fooId: Foo.FooId.T, b1: String)
object Bar {
object BarId extends Opaque[UUID] {
type T = UUID ## Tag
}
import Foo.FooId._
import BarId._
implicit val format: Format[Bar] = Json.format[Bar]
}
I have the following error from the compiler:
implicit val format: Format[Bar] = Json.format[Bar]
^
<pastie>:43: error: No instance of play.api.libs.json.Format is available for Opaque.<refinement>, Opaque.<refinement> in the implicit scope (Hint: if declared in the same file, make sure it's declared before)
I'm not able to explain why I'm having this behaviour, the error message is not explicit. I'm importing the Format for FooId and BarId needed for deriving a format for Bar class.
The thing is that names of implicits are significant.
Very simple example of that is following:
object MyObject {
implicit val i: Int = ???
}
import MyObject._
implicit val i: String = ???
// implicitly[Int] // doesn't compile
// implicitly[String] // doesn't compile
but
object MyObject {
implicit val i: Int = ???
}
import MyObject._
implicit val i1: String = ???
implicitly[Int] // compiles
implicitly[String] // compiles
If you want derivation Json.format[Bar] to work, there should be implicits Format[Bar.BarId.T], Format[Foo.FooId.T] in scope i.e. Format instances for fields of Bar. If you make the only import
import Foo.FooId._
implicitly[Format[Foo.FooId.T]] // compiles
and
import BarId._
implicitly[Format[Bar.BarId.T]] // compiles
but if you import both, since names of implicits collide
import Foo.FooId._
import BarId._
// implicitly[Format[Foo.FooId.T]] // doesn't compiles
// implicitly[Format[Bar.BarId.T]] // doesn't compiles
For example you can move trait Tag outside trait Opaque and make the only import. Then
implicitly[Format[Foo.FooId.T]]
implicitly[Format[Bar.BarId.T]]
Json.format[Bar]
will compile.
https://youtu.be/1h8xNBykZqM?t=681 Some Mistakes We Made When Designing Implicits, Mistake #1
NullPointerException on implicit resolution
I was playing with implicits but I got some behaviour that I don't understand.
I defined a simple class and its companion object (just to make a quick test on implicits) as follows
class SimpleNumber(val numb: Int) {
def sum(n: Int) = numb + n
}
object SimpleNumber {
implicit def stringToInt(s: String) = s.toInt
}
The method stringToInt is supposed to work in the event I call the sum method by passing a string instead of an int, so that it can be converted to an int (it's just for testing purposes, so I don't need to check errors or exceptions).
If I paste the above code in the repl (:paste), I get this warning
warning: implicit conversion method stringToInt should be enabled
by making the implicit value scala.language.implicitConversions visible.
This can be achieved by adding the import clause 'import scala.language.implicitConversions'
or by setting the compiler option -language:implicitConversions.
So I moved to VSCode and pasted in the same code, to see if I could get some more info via metals plugin, but that warning doesn't even pop out. Then I created a new object that extends the App trait to test the code like this
object TestDriver extends App{
val a = new SimpleNumber(4)
println(a.sum("5"))
}
but I received the following error
type mismatch; found : String("5") required: Int
I tried importing the implicitConversions as the repl suggested, first in the companion object and then in the TestDriver object, but to no avail. Then I imported the implicit method directly in the TestDriver object, and that worked.
import SimpleNumber.stringToInt
object TestDriver extends App{
val a = new SimpleNumber(4)
println(a.sum("5"))
}
Why doesn't the import scala.language.implicitConversions work as I thought it would? I'm using scala 2.13.3
Why doesn't the import scala.language.implicitConversions work as I thought it would?
import scala.language.implicitConversions is just to signal that implicit conversions are defined in a code, not to bring specific ones to a scope. You can do
import SimpleNumber._
to bring stringToInt to the local scope.
Where does Scala look for implicits?
Should I use method overload then?
Depends on your goal. Implicit conversions (which are normally not recommended) are not just for method overloading. In your example String can be used in all places where Int is expected (this is just for example, normally standard types like Int, String, Function shouldn't be used for implicits, use custom types for implicits). If you used implicit conversions just for method overloading then yes, you should prefer the latter. Don't forget that method overloading can be done not only as
class SimpleNumber(val numb: Int) {
def sum(n: Int): Int = numb + n
def sum(n: String): Int = numb + n.toInt
}
but also with a type class
class SimpleNumber(val numb: Int) {
def sum[A: ToInt](n: A): Int = numb + n.toInt
}
trait ToInt[A] {
def toInt(a: A): Int
}
object ToInt {
implicit val int: ToInt[Int] = identity
implicit val str: ToInt[String] = _.toInt
}
implicit class ToIntOps[A](val a: A) extends AnyVal {
def toInt(implicit ti: ToInt[A]): Int = ti.toInt(a)
}
or magnet
import scala.language.implicitConversions
import Predef.{augmentString => _, _} // to avoid implicit ambiguity
class SimpleNumber(val numb: Int) {
def sum(n: ToInt): Int = numb + n.toInt()
}
trait ToInt {
def toInt(): Int
}
object ToInt {
implicit def fromInt(i: Int): ToInt = () => i
implicit def fromStr(s: String): ToInt = () => s.toInt
}
Notice that you don't have to import ToInt.
Overloading methods based on generics
Type erasure problem in method overloading
I have an implicit class defined in a class injected in other classes like this
class A {
implicit class B(s: String) {
def b = ???
}
}
class C(a: A) {}
Is there a way to access implicit class B (and in particular its method b) from the class C, without importing it explicitly? (Please note that class A could not be a trait since it also injects some classes.)
Solution 1 (import a._)
Well, yes, as already noted in the comments, from your requirements it is not obvious why you wouldn't just import a._ in the body of C:
class A {
implicit class B(arg: String) {
def b: String = ???
}
}
class C(a: A) {
import a._
{
println("hello".b)
}
}
This one line really doesn't hurt anyone.
If you still don't like it, then the problem might be elsewhere. Thus my second proposal.
Solution 2 (separating typeclass-like A-interface from .b-syntax)
This other solution is less about the reduction of number of imports in your code, and it doesn't even keep class B inside A. But it might address another issue that you maybe just can't quite articulate: it separates the functionality provided by A from the syntax provided by B.
The structure of the following snippet is inspired by the design of the Scala Cats library, that follows a very clear policy with the implicit declarations, always separating the typeclass defintions from the syntax.
The main idea is that:
Implementations of AIntf provide actual functionality
B provides only some additional "pimp-my-library"-style methods
and that we want to keep these two things separate.
Here is how to separate them, thereby also avoiding import a._ inside of C. First, you define the interface that describes the functionality provided by A:
trait AIntf {
def usefulOperationOnStrings(s: String): String
}
Then you can implement it by a few different A`s:
class A extends AIntf {
def usefulOperationOnStrings(s: String): String = "<foo>" + s + "</foo>"
}
class A2 extends AIntf {
def usefulOperationOnStrings(s: String): String = s.toUpperCase
}
Note that the object B has disappeared from A. Instead, it is moved in a separate syntax-package, and renamed to A_Ops. The method b is also renamed to a:
object syntax /* should be package, object only for script */ {
object a {
class A_Ops(wrapped: String, ai: AIntf) {
def a: String = ai.usefulOperationOnStrings(wrapped)
}
implicit def everyStringHasAOps(s: String)(implicit ai: AIntf): A_Ops = {
new A_Ops(s, ai)
}
}
}
This is how you use it:
You say in the imports that you want to refer to interface A_Intf
You say in the imports that you want to use the syntax syntax.a._
You declare the a-argument of C as implicit
Then you can just use "string".a syntax inside C without further imports.
In code:
import myproject.AIntf
import myproject.syntax.a._
class C(implicit val a: AIntf) {
{
println("hello".a)
}
}
Now the implementations of AIntf and the syntax .a become independent. You can inject A2 instead of A. Or you can change the syntax from "str".a to "str".somethingEntirelyDifferent.
The full code snippet:
import scala.language.implicitConversions
object myproject /* should be package, object only for script */ {
trait AIntf {
def usefulOperationOnStrings(s: String): String
}
object syntax /* should be package, object only for script */ {
object a {
class A_Ops(wrapped: String, ai: AIntf) {
def a: String = ai.usefulOperationOnStrings(wrapped)
}
implicit def everyStringHasAOps(s: String)(implicit ai: AIntf): A_Ops = {
new A_Ops(s, ai)
}
}
}
class A extends AIntf {
def usefulOperationOnStrings(s: String): String = "<foo>" + s + "</foo>"
}
class A2 extends AIntf {
def usefulOperationOnStrings(s: String): String = s.toUpperCase
}
}
import myproject.AIntf
import myproject.syntax.a._
class C(implicit val a: AIntf) {
{
println("hello".a)
}
}
val c1 = new C()(new myproject.A)
val c2 = new C()(new myproject.A2)
// prints:
// <foo>hello</foo>
// HELLO
Unfortunately, I have no clue what guice is going to do with an implicit argument, have not tried it yet. It might force you to write
class C #Inject()(val a: AIntf) {
implicit aintf: AIntf = a
...
}
which then becomes longer then the simple import mentioned in the first part.
as noted in comment, just import a._:
class A {
implicit class B(s: String) {
def b: String = "hello "+ s
}
}
class C(a: A){
import a._
val hello = "world".b
}
val c = new C(new A)
c.hello // "hello world"
Why cannot I load the file with the following code in spark-shell
import org.apache.spark.sql.types._
import org.apache.spark.sql.Encoder import org.apache.spark.sql.Encoders
import org.apache.spark.sql.expressions.Aggregator
case class Data(i: Int)
val customSummer = new Aggregator[Data, Int, Int] {
def zero: Int = 0
def reduce(b: Int, a: Data): Int = b + a.i
def merge(b1: Int, b2: Int): Int = b1 + b2
def finish(r: Int): Int = r
}.toColumn()
The error:
<console>:47: error: object creation impossible, since:
it has 2 unimplemented members.
/** As seen from <$anon: org.apache.spark.sql.expressions.Aggregator[Data,Int,Int]>, the missing signatures are as follows.
* For convenience, these are usable as stub implementations.
*/
def bufferEncoder: org.apache.spark.sql.Encoder[Int] = ???
def outputEncoder: org.apache.spark.sql.Encoder[Int] = ???
val customSummer = new Aggregator[Data, Int, Int] {
Update: #user8371915's solution works. But the following script cannot be loaded with a different error. I used :load script.sc in the spark-shell.
import org.apache.spark.sql.expressions.Aggregator
class MyClass extends Aggregator
Error:
loading ./script.sc...
import org.apache.spark.sql.expressions.Aggregator
<console>:11: error: not found: type Aggregator
class MyClass extends Aggregator
Update(2017-12-03): it doesn't seem to work within Zeppelin, either.
As per error message you didn't implement bufferEncoder and outputEncoder. Please check API docs for the list of abstract methods that have to be implemented.
These two should suffice:
def bufferEncoder: Encoder[Int] = Encoders.scalaInt
def outputEncoder: Encoder[Int] = Encoders.scalaInt
Is there a way to access the constructor declaration of a macro annotation from within the macro implementation by reflection? For example, when I define this macro annotation:
class Component(x: String, y: Int = 0) extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro ComponentMacro.impl
}
is it possible to reflect on the Component from within the macro implementation? When I use something like:
class ComponentMacro(val c: whitebox.Context) {
import c.universe._
def impl(annottees: c.Expr[Any]*) = {
/* ... */
val t = typeOf[Component] // throws exception
}
}
the compiler throws an error during macro expansion.
You can use c.prefix.tree, as in the example below:
class ComponentMacro(val c: scala.reflect.macros.whitebox.Context) {
import c.universe._
def impl(annottees: c.Expr[Any]*): c.Expr[Any] = {
val annottation = c.prefix.tree
annottation match {
case q""" new $className(..$params) """ => println(s"className=$className, x=$params")
}
c.Expr(q""" case class Test() """)
}
}
For this annotated class:
#Component("FooBar", 5)
case class Test()
It will output:
Warning:scalac: className=Component, x=List("FooBar", 5)