Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
200 views
in Technique[技术] by (71.8m points)

Understand Scala Implicit classes

I am reading a Spark in Action book, I came across a construct that even after a while working with Scala I still can't understand. Here is the code (complete code) :

First we have a case class where RDD will be converted to DataFrame using this case class:

     import java.sql.Timestamp
        case class Post(
          commentCount:Option[Int],
          lastActivityDate:Option[java.sql.Timestamp],
          ownerUserId:Option[Long],
          body:String,
          score:Option[Int],
     ... ) 

Then we define an object with implicit class

object StringImplicits {

    implicit class StringImprovements(val s: String) {
    import scala.util.control.Exception.catching

    def toIntSafe = catching(classOf[NumberFormatException]) opt s.toInt

    def toLongSafe = catching(classOf[NumberFormatException]) opt s.toLong

    def toTimestampSafe = catching(classOf[IllegalArgumentException]) opt
      Timestamp.valueOf(s)
  }

}

Then we import the object which is already in scope for some reason and now looks like all String object has the three methods defined in StringImprovements

import StringImplicits._

def stringToPost(row: String): Post = {
  val r = row.split("~")
  Post(r(0).toIntSafe,
    r(1).toTimestampSafe,
    r(2).toLongSafe,
    r(3),
    ... 
}

Questions:

  • Why do we need to need to import an already in scope object?
  • Which statement exactly assigned these methods to the String object, this is so confusing?

Reading implicit classes doc it makes some sense but not everything is clear. For example how come this is a valid expression

scala> 5 times println("HI")

I understand if it was

IntWithTimes(5).time(println("HI")) 
question from:https://stackoverflow.com/questions/65844327/understand-scala-implicit-classes

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Implicits are sought among all methods and values that are visible in the current scope AND within companion objects of types that appear in the implicit type you are looking for.

implicit val a: A = ???

implicit val bc: B[C] = ???

// code here sees a and bc in implicit scope

def helper = {

  implicit val c: C = ???

  // code here sees a and bc and c in implicit scope
}

def anotherHelper = {

  val a: A = ??? // overshades a
  
  // code here sees b in implicit scope
}

object Helper {

  implicit val e: E = ???

  // code here sees a, bc and e in implicit scope
}

// implicits within helper, anotherHelper and Helper are not seen here
// though Helper one's can be imported with import Helper._
class D[T]
object {

  implicit def provide[T](implicit a: T): D[T] = ???
}

class E
object E {
  implicit val e: E = ???
}

class F
object F

// sees no implicits that are always available but

// if you require implicitly[E], E companion will be searched
// and implicit e will be found

// if you require implicitly[D[F]], both D and F companions
// will be searched, D.provide[F] will require implicit F,
// and no such implicit will be found - failure 

If you asked for implicit A[B, C, (D, E), F[G]], then Scala would look into companions of A, B, C, D, E, Tuple2, F, G (if they exist). If this rule doesn't apply (your implicits aren't in companion, your type doesn't appear in sought implicit) you have to import them manually.

When Scala sees that you try to call a method on object that doesn't exists, it will try to convert it to some object which has this method. If you have some X type, it will look for implicits your X and returning some Y (implicit defs(x: X): Y and implicit val f: X => Y) and checking if that Y has this method.

Implicit class is basically a combination of a class and implicit method:

// implicit class Y(x: X) is the same as
class Y(x: X)
implicit def convert(x: X): Y = new Y(x)

which means that you can add some implicit conversions/classes from type X into X's companion and they will be always available.

In docs:

implicit class IntWithTimes(x: Int)

created implicit conversion from Int => IntWithTimes. String doesn't have times method, but there is a conversion Int => IntWithTimes in implicit scope imperted with import Helper._ (without it compilation would fail). Its result, IntWithTimes has the times method with the right signature, so the conversion is used.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...