I am trying to understand the implicit method in Scala, in the SlashSyntax.scala I defined a customed operator method:
package com.example.hello.impl
object SlashSyntax {
final class IntOps(private val intSrc: Int) {
def \(a2: Int): Int = (intSrc.toString + a2.toString).toInt
}
}
trait SlashSyntax {
import SlashSyntax._
implicit def intOps(src: Int) = new IntOps(src)
}
then in the main method I try to use the operator but it says cannot resolve symbol :
object Test {
def main(args: Array[String]): Unit = {
import com.example.hello.impl.SlashSyntax._
val s = 10 \ 2 //cannot resolve symbol \
}
}
How can I use my own operator?
The simplest solution is to make the IntOps class an implicit class. Then
you don't need the SlashSyntax trait (or you can use it for other things)
the implicit is in the right place to be imported
the pesky implicit conversion method ... warning goes away
The issue caused by incorrect import.
import com.example.hello.impl.SlashSyntax._
Problem here is that you imported all members from object SlashSyntax instead of trait SlashSyntax. This happened because scala considered com.example.hello.impl.SlashSyntax as path. All paths can be composed only from Stable members. Traits are not Stable members but objects are stable members, so scala imported members from object SlashSyntax.
You can just move your implicit method to object SlashSyntax and this will fix the issue.
Your object SlashSyntax doesn't extends trait SlashSyntax so your extension methods are not seen when you do SlashSyntax._.
Additionally you didn't do import scala.languageFeatures.implicitConversions. though you might have added -language:implicitConversions flag to scalac.
Sometimes lack of type annotation break things. Not in your case but it's good in general to these things there.
This works:
object SlashSyntax extends SlashSyntax {
final class IntOps(private val intSrc: Int) {
def \(a2: Int): Int = (intSrc.toString + a2.toString).toInt
}
}
trait SlashSyntax {
import SlashSyntax.IntOps
implicit def intOps(src: Int): IntOps = new IntOps(src)
}
Related
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 a package foo which contains class FStream. The package object of foo defines a few implicit value classes that provide extender methods for FStream. I would like to move these value classes out of the package object and into their own individual files, but I also want them to always be available when I use FStream (or preferably, when I use anything from foo package. Is it possible to accomplish this? I tried putting implicit value classes into other objects, but I can't extend from objects. Tried putting them in classes or traits, but implicit value classes can only be defined in other objects.
foo/FStream.scala
package foo
class FStream {
def makeFoo(): Unit = ???
}
foo/package.scala
package foo
package object foo {
// I want to move these definitions into separate files:
implicit class SuperFoo(val stream: FStream) extends AnyVal {
def makeSuperFoo(): Unit = ???
}
implicit class HyperFoo(val stream: FStream) extends AnyVal {
def makeHyperFoo(): Unit = ???
}
}
bar/usage.scala
package bar
import foo._ // something nice and short that doesn't reference individual value classes
val x: FStream = ???
x.makeSuperFoo() // should work
x.makeHyperFoo() // should work
I recommend you to read the mandatory tutorial first.
My solution is to use FStream's companion object. So you can just import FStream and get all the functionality. This also uses trait to separate files.
foo/FStream.scala
package foo
class FStream {
def makeFoo(): Unit = ???
}
// companion provides implicit
object FStream extends FStreamOp
foo/FStreamOp.scala
package foo
// value class may not be a member of another class
class SuperFoo(val stream: FStream) extends AnyVal {
def makeSuperFoo(): Unit = ???
}
class HyperFoo(val stream: FStream) extends AnyVal {
def makeHyperFoo(): Unit = ???
}
trait FStreamOp {
// you need to provide separate implicit conversion
implicit def makeSuper(stream: FStream) = new SuperFoo(stream)
implicit def makeHyper(stream: FStream) = new HyperFoo(stream)
}
usage.scala
import foo.FStream
object Main {
def main(args: Array[String]): Unit = {
val x: FStream = ???
x.makeSuperFoo() // should work
x.makeHyperFoo() // should work
}
}
When someone imports my object, I would like some implicit classes from other objects (or packages) to be available to them. Having import in the object does not help, as imports are not imported transitively, therefore I assume I must use some implicit def or val, however I am unable to find some reasonable way how to achieve this, only a quite verbose def:
object Main extends App {
object A {
implicit class ExtendedMath(val x: Double) extends AnyVal {
def square = x * x
}
}
object X {
import A._
// what to write here, so that ExtendedMath is forwarded to our users?
// following works, but it seems quite verbose
implicit def extendedMath(x: Double): ExtendedMath = ExtendedMath(x)
}
import X._
val a = 0.0
println(a.square)
}
Is there some more concise way?
As #stew suggests, the typical approach is to define the implicits in a trait that you can then mix in multiple times. The only caveat is that value classes are not allowed inside a trait, so you have to resort to separating value class and implicit conversion:
class ExtendedMath(val x: Double) extends AnyVal {
def square = x * x
}
trait HasMath {
// note: method name somehow must not shadow
// value class name, therefore we use lower-case
implicit def extendedMath(x: Double): ExtendedMath = new ExtendedMath(x)
}
object A extends HasMath
object X extends HasMath
object Test {
import X._
4.0.square
}
I'm trying to provide different sets of implementations for a list of type classes, where importing different package objects would give the end user a different version of a TypeClass implementation into scope, with the swap being completely invisible.
trait TypeClass[T] {
def name: String
}
trait DefaultTypeClasses {
implicit val String: TypeClass[String]
implicit val Int: TypeClass[Int]
}
trait FirstImplementor extends DefaultTypeClasses {
implicit object String extends TypeClass[String] {
def name = "test"
}
implicit object Int extends TypeClass[Int] {
def name = "int"
}
}
object FirstImplementor extends FirstImplementor
object Test {
import FirstImplementor._
def doSomething[T : TypeClass](value: T): Unit = {
println(implicitly[TypeClass[T]].name)
}
doSomething("test")
}
The above works as expected, but if do:
trait DefaultDefinitions extends DefaultTypeClasses {
}
package object something extends DefaultDefinitions with FirstImplementor {}
And then I import the same package object into the scope of the Test object, like this:
import com.blabla.something._ // should bring all type class definitions from FirstImplementor into scope.
object Test {
def doSomething[T : TypeClass](value: T): Unit = {
println(implicitly[TypeClass[T]].name)
}
doSomething("test")// Cannot find implicit value for TypeClass[String]
doSomething[String]("test")(String) // if passed explicitly it compiles as expected, no more imports necessary.
}
For whatever reason, the materialised type class is available in explicit scope by name, but not in implicit scope. Sounds like my knowledge of the SLS is experiencing a gap, could anyone please clarify?
But if you create Implicits object in package, it works fine. However I have no idea if original error is a bug in scalac or correct behavior
package object something {
object Implicits extends DefaultDefinitions with FirstImplementor
}
import something.Implicits._
I thought implicits in the companion object would be found. What's wrong with this?
object Elsewhere{
case class NamedInt(i: Int, name: String)
object NamedInt{
implicit class ToNamedInt(i: Int){
def named(name: String) = NamedInt(i, name)
}
}
}
object Application{
import Elsewhere.NamedInt
//Error: value named is not a member of Int
val named: NamedInt = 3.named("bob")
}
Update: I realise I can import the implicit class directly, but I thought it should compile without it since the implicit is in the companion object. E.g. this works without an extra import
object Elsewhere{
case class MyInt(i: Int)
object MyInt{
import scala.language.implicitConversions
implicit def myIntToSome(t: MyInt): Some[Int] = Some(t.i)
}
}
object Application{
import Elsewhere.MyInt
val o: Option[Int] = MyInt(1)
}
Update 2:
Jesse Eichar comments on his blog:
You are confusing implicit parameter resolution with implicit object
conversion. Implicit object conversion is potentially dangerous so
they normally have to be imported explicitly into scope.
Caveats to that is implicit object conversions defined in superclasses
and (I am pretty sure) in package objects are automatically in scope.
This would make sense to me, but then why does the MyInt example above work?
Adding one extra line fixes things (you need to also explicitly import the companion object fields):
object Application{
import Elsewhere.NamedInt
import Elsewhere.NamedInt._
// Compiles OK now :)
val named: NamedInt = 3.named("bob")
}