Problem with bringing into scope scala implicit conversions - scala

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

Related

how to use implicit method in trait

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)
}

How to fix this typeclass example?

This is a follow-up to my previous question:
Suppose I create the following test converter.scala:
trait ConverterTo[T] {
def convert(s: String): Option[T]
}
object Converters {
implicit val toInt: ConverterTo[Int] =
new ConverterTo[Int] {
def convert(s: String) = scala.util.Try(s.toInt).toOption
}
}
class A {
import Converters._
def foo[T](s: String)(implicit ct: ConverterTo[T]) = ct.convert(s)
}
Now when I tried to call foo in REPL it fails to compile:
scala> :load converter.scala
Loading converter.scala...
defined trait ConverterTo
defined module Converters
defined class A
scala> val a = new A()
scala> a.foo[Int]("0")
<console>:12: error: could not find implicit value for parameter ct: ConverterTo[Int]
a.foo[Int]("0")
^
import Converters._ in class A does not cut it. You can remove it and the code will still compile. The moment the compiler needs to find in actual implicit is not in class A, where foo is just declared.
The compiler needs to find a ConverterTo[Int] in implicit scope at the moment you call a.foo[Int](..) that is in the REPL. So this is where the import needs to be.
Had object Converters and trait ConverterTo been named the same (so there would be a companion object) the import would not be needed.

Scala: Multiple implicit conversions with same name

Using scala 2.10.3, my goal is to make the following work:
object A {
implicit class Imp(i: Int) {
def myPrint() {
println(i)
}
}
}
object B {
implicit class Imp(i: String) {
def myPrint() {
println(i)
}
}
}
import A._
import B._
object MyApp extends App {
3.myPrint()
}
This fails with
value myPrint is not a member of Int
If I give A.Imp and B.Imp different names (for example A.Imp1 and B.Imp2), it works.
Diving a bit deeper into it, there seems to be the same problem with implicit conversions.
This works:
object A {
implicit def Imp(i: Int) = new {
def myPrint() {
println(i)
}
}
implicit def Imp(i: String) = new {
def myPrint() {
println(i)
}
}
}
import A._
object MyApp extends App {
3.myPrint()
}
Whereas this doesn't:
object A {
implicit def Imp(i: Int) = new {
def myPrint() {
println(i)
}
}
}
object B {
implicit def Imp(i: String) = new {
def myPrint() {
println(i)
}
}
}
import A._
import B._
object MyApp extends App {
3.myPrint()
}
Why? Is this a bug in the scala compiler? I need this scenario, since my objects A and B derive from the same trait (with a type parameter) which then defines the implicit conversion for its type parameter. In this trait, I can only give one name for the implicit conversion. I want to be able to import more of these objects into my scope. Is there a way to do that?
edit: I can't give the implicit classes different names, since the examples above are only breaking down the problem. My actual code looks more like
trait P[T] {
implicit class Imp(i: T) {
def myPrint() {
...
}
}
}
object A extends P[Int]
object B extends P[String]
import A._
import B._
The implicits just have to be available as a simple name, so you can rename on import.
Just to verify:
scala> import A._ ; import B.{ Imp => BImp, _ }
import A._
import B.{Imp=>BImp, _}
scala> 3.myPrint
3
Actually, it works if you replace
import A._
import B._
with
import B._
import A._
What happens, I think, is that A.Imp is shadowed by B.Imp because it has the same name. Apparently shadowing applies on the function's name and do not take the signature into account.
So if you import A then B, then only B.Imp(i: String) will be available, and if you import B then A, then only A.Imp(i: Int) will be available.
If you need to use both A.Imp and B.Imp, you must rename one of them.
In case anyone else runs into this issue, there is a partial workaround, which I found here:
https://github.com/lihaoyi/scalatags/blob/3dea48c42c5581329e363d8c3f587c2c50d92f85/scalatags/shared/src/main/scala/scalatags/generic/Bundle.scala#L120
That code was written by Li Haoyi, so you can be pretty confident that no better solution exists...
Essentially, one can still use traits to define methods in terms of each other, but it will require boilerplate to copy those implicits into unique names. This example might be easier to follow:
trait Chainable
object Chainable {
implicit val _chainableFromInt = IntChainable.chainable _
implicit val _chainableFromIntTrav = IntChainable.traversable _
implicit val _chainableFromIntOpt = IntChainable.optional _
implicit val _chainableFromIntTry = IntChainable.tried _
implicit val _chainableFromDom = DomChainable.chainable _
implicit val _chainableFromDomTrav = DomChainable.traversable _
implicit val _chainableFromDomOpt = DomChainable.optional _
implicit val _chainableFromDomTry = DomChainable.tried _
private object IntChainable extends ImplChainables[Int] {
def chainable(n:Int) = Constant(n)
}
private object DomChainable extends ImplChainables[dom.Element]{
def chainable(e:Element) = Insertion(e)
}
private trait ImplChainables[T] {
def chainable(t:T):Chainable
def traversable(trav:TraversableOnce[T]):Chainable =
SeqChainable(trav.map(chainable).toList)
def optional(opt:Option[T]):Chainable =
opt match {
case Some(t) => chainable(t)
case None => NoneChainable
}
def tried(tried:Try[T]):Chainable =
optional(tried.toOption)
}
}
In other words, never write:
trait P[T] {
implicit def foo(i: T) = ...
}
object A extends P[X]
Because defining implicits in type parameterized traits will lead to these naming conflicts. Although, incidentally, the trait in mentioned in the link above is parameterized over many types, but idea there is that none of the implementations of that trait are ever needed in the same scope. (JSDom vs Text, and all._ vs short._ for those familiar with Scalatags)
I also recommend reading: http://www.lihaoyi.com/post/ImplicitDesignPatternsinScala.html
It does not address this issue specifically but is an excellent summary of how to use implicits.
However, putting all these pieces together, this still seems to be an issue:
trait ImplChainables[AnotherTypeClass]{
type F[A] = A=>AnotherTypeClass
implicit def transitiveImplicit[A:F](t: A):Chainable
implicit def traversable[A:F](trav: TraversableOnce[A]):Chainable = ...
}
What this trait would allow is:
object anotherImpl extends ImplChainables[AnotherTypeClass] {...}
import anotherImpl._
implicit val string2another: String=>AnotherTypeClass = ...
Seq("x"):Chainable
Because of the type parameter and the context binding (implicit parameter) those cannot be eta-expanded (i.e: Foo.bar _ ) into function values. The implicit parameter part has been fixed in Dotty: http://dotty.epfl.ch/blog/2016/12/05/implicit-function-types.html
I do not know if a complete solution would be possible, needless to say this is a complex problem. So it would be nice if same name implicits just worked and the whole issue could be avoided. And in any case, adding an unimport keyword would make so much more sense than turning off implicits by shadowing them.

Unable to deal with "traits cannot have type parameters with context bound"

I a have the models:
case class MyModel1(id: UUID, a: Int, b: String...)
case class MyModel2(id: Long, c: Int, d: String...)
And I want some of them to be able to convert themselves to JSON.
trait Jsonable[T] {
import play.api.libs.json._
implicit val writer: Writes[T] = Json.writes[T]
}
trait JsonableId[TId, T] extends Jsonable[T] {
implicit val pkIdWriter = Writes[Pk[TId]] { pkId => JsString(pkId.get.toString) }
implicit val idWriter = Writes[TId] { id => JsString(id.toString) }
}
And when I say:
object MyModel1 extends JsonableId[Long, MyModel1] {
def single(a: Long) = //.....
}
It throws an error No unapply function found. Well, I tried to do this:
trait Jsonable[T: {def unapply(a: T): Option[T]}] {
import play.api.libs.json._
implicit val writer: Writes[T] = Json.writes[T]
}
But the error was traits cannot have type parameters with context bound.
How do I deal with this?
This will not work just because macro-implemented method Json.writes has not enough information about T.
No unapply function found is not the only error message you'll get, it's just the first one.
If you want to use duck typing you should use T <: {def unapply(a: T): Option[T]}. With T: {def unapply(a: T): Option[T]} you are trying to add an implicit parameter of type {def unapply(a: T): Option[T]}[T] and this makes no sense.
Any way: it will not help you: writesImpl macro can't find an unapply method in companion object, not in T itself. I guess it should fail even earlier with error like No companion object found or even Call this method with case class name as type parameter.
So the answer: don't try to do this. Just add implicit val writer: Writes[T] = Json.writes[MyModel1] manually.
The only way to implement "something like this" is to add such implicit parameter into companion object using macro-implemented annotation like this:
#withWrites case class MyModel1(id: UUID, a: Int, b: String...)
#withWrites case class MyModel2(id: Long, c: Int, d: String...)

Implicit conversion with implicit parameter

I'm implementing a Java interface with a lot of methods with Object parameters, which in my case are really Strings containing user names:
public interface TwoFactorAuthProvider {
boolean requiresTwoFactorAuth(Object principal);
... //many more methods with the same kind of parameter
}
I'm trying to use implicit conversion to convert these to User objects in my implementation:
class TwoFactorAuthProviderImpl(userRepository: UserRepository)
extends TwoFactorAuthProvider {
def requiresTwoFactorAuth(user: User): Boolean = {
...
}
}
When I define the conversion in the companion object of my class, it is picked up just fine and my class compiles:
object TwoFactorAuthProviderImpl {
implicit def toUser(principal: Any): User = {
null //TODO: do something useful
}
}
However, to be able to do the conversion, I need access to the user repository, which the TwoFactorAuthProviderImpl instance has, but the companion object does not. I thought I could possibly use an implicit parameter to pass it:
implicit def toUser(principal: Any)(implicit repo: UserRepository): User = {
val email = principal.asInstanceOf[String]
repo.findByEmail(email)
}
But with the implicit parameter, the conversion is no longer picked up by the compiler (complaining that I'm not implementing the interface).
Is there a way to get the implicit conversion that I want, or is this outside the scope of what you can do with implicits?
This should work just fine - can you supply the exact compilation error? Not implementing what interface? It looks like you would have to declare as follows:
class TwoFactorAuthProviderImpl(implicit userRepository: UserRepository)
Here's an example for the REPL to show that implicits can have implicits; I'm using paste mode to ensure that module X is the companion object of the class X
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class X(i: Int, s: String)
object X { implicit def Int_Is_X(i: Int)(implicit s: String) = X(i, s) }
// Exiting paste mode, now interpreting.
defined class X
defined module X
scala> val i: X = 4
<console>:9: error: value X is not a member of object $iw
val i: X = 4
^
But if we add an implicit string in scope
scala> implicit val s = "Foo"
s: java.lang.String = Foo
scala> val i: X = 4
i: X = X(4,Foo)
Implicits advice
Don't go overboard with implicit conversions - I think you are going too far in this sense - the principal is implicitly a mechanism by which you can discover a user, it is not implicitly a user itself. I'd be tempted to do something like this instead:
implicit def Principal_Is_UserDiscoverable(p: String) = new {
def findUser(implicit repo: UserRepository) = repo.findUser(p)
}
Then you can do "oxbow".findUser
Thanks to Oxbow's answer, I now have it working, this is only for reference.
First of all, a value that should be passed as an implicit must itself be marked implicit:
class TwoFactorAuthProviderImpl(implicit userRepository: UserRepository) ...
Second, implicit conversions are nice and all, but a method implementation signature must still match the signature of its declaration. So this does not compile, even though there is a conversion from Any to User:
def requiresTwoFactorAuth(principal: User): Boolean = { ... }
But leaving the parameter as Any, as in the declaration, and then using it as a user works just fine:
def requiresTwoFactorAuth(principal: Any): Boolean = {
principal.getSettings().getPhoneUsedForTwoFactorAuthentication() != null
}
Also, the conversion really doesn't have to be in the companion object in this case, so in the end, I left the implicit parameters out.
The full source code:
class TwoFactorAuthProviderImpl(userRepository: UserRepository)
extends TwoFactorAuthProvider {
private implicit def toUser(principal: Any): User = {
val email = principal.asInstanceOf[String]
userRepository.findByEmail(email)
}
def requiresTwoFactorAuth(principal: Any): Boolean = {
//using principal as a User
principal.getSettings().getPhoneUsedForTwoFactorAuthentication() != null
}
...
}