Expand Code to Raw AST Scala 3 in the REPL - scala

I am currently doing a lot of compiler research. This not only entails to writing compiler plugins but also modifying the dotty compiler, from the parser to the typer. Therefore I need to constantly look at the raw ASTs to sketch the necessary transformations.
In Scala 2, the reflection library provided the following functionality:
val expression = ....
val tree = reify{expression}.tree
showRaw(tree)
Now from what I understand from the docs, the final step has been replaced by Printer.TreeStructure.show(tree)
However, I can not find anything in the meta programming docs for an alternative to reify. Now I can obviously use various meta programming techniques inside a Scala program and print the tree to stdout but this is a very time consuming process compared to expanding in the REPL for a quick manual verification.
Is there a way to do this in the Scala 3 REPL?

You can define your own reify in your project
import scala.quoted.*
object App {
inline def reify(inline a: Any) = ${reifyImpl('a)}
def reifyImpl(a: Expr[Any])(using Quotes): Expr[String] = {
import quotes.reflect.*
Literal(StringConstant(Printer.TreeStructure.show(a.asTerm))).asExprOf[String]
}
}
and use it in REPL
sbt console
scala> import App.reify
scala> reify{ class A }
val res0: String = Inlined(None, Nil, Block(List(ClassDef("A", DefDef("<init>", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), None, Nil)), Literal(UnitConstant())))
Unfortunately defining reify directly in REPL doesn't seem to work
scala> import scala.quoted.*; inline def reify(inline a: Any) = ${reifyImpl('a)}; def reifyImpl(a: Expr[Any])(using Quotes): Expr[String] = {import quotes.reflect.*; Literal(StringConstant(Printer.TreeStructure.show(a.asTerm))).asExprOf[String]}
def reify(a: Any): String
def reifyImpl
(a: quoted.Expr[Any])(using x$2: quoted.Quotes): quoted.Expr[String]
scala> reify{class A}
-- Error:
1 |reify{class A}
|^^^^^^^^^^^^^^
|Failed to evaluate macro.
| Caused by class java.lang.ClassNotFoundException: rs$line$6
| java.lang.ClassLoader.loadClass(ClassLoader.java:418)
| java.lang.ClassLoader.loadClass(ClassLoader.java:351)
| dotty.tools.repl.AbstractFileClassLoader.loadClass(AbstractFileClassLoader.scala:55)
| dotty.tools.dotc.transform.Splicer$Interpreter.loadReplLineClass(Splicer.scala:402)
| dotty.tools.dotc.transform.Splicer$Interpreter.interpretedStaticMethodCall(Splicer.scala:354)
| dotty.tools.dotc.transform.Splicer$Interpreter.interpretTree(Splicer.scala:260)
| dotty.tools.dotc.transform.Splicer$Interpreter.interpretTree$$anonfun$2(Splicer.scala:281)
|
| This location contains code that was inlined from rs$line$7:1
In 3.2.0 reify can be defined directly in REPL (scala-cli)
$ scala-cli
Welcome to Scala 3.2.0 (1.8.0_231, Java Java HotSpot(TM) 64-Bit GraalVM EE 19.3.0).
Type in expressions for evaluation. Or try :help.
scala> import scala.quoted.*; inline def reify(inline a: Any) = ${reifyImpl('a)}; def reifyImpl(a: Expr[Any])(using Quotes): Expr[String] = {import quotes.reflect.*; Literal(StringConstant(Printer.TreeStructure.show(a.asTerm))).asExprOf[String]}
def reify(a: Any): String
def reifyImpl
(a: quoted.Expr[Any])(using x$2: quoted.Quotes): quoted.Expr[String]
scala> reify{class A}
val res0: String = Inlined(None, Nil, Block(List(ClassDef("A", DefDef("<init>", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), None, Nil)), Literal(UnitConstant())))

Related

spark-shell "error: type mismatch" using :paste to define class/companion object

Using spark-shell v1.6.
Big differences when I load a class def and its companion object depending on how it's done.
one line at a time - OK
via :paste - PROBLEM
First things first... since the repl finds it ambiguous to load a companion object with the same name, I give it an altered name. No problem there. Setup lookes like this...
import scala.util.Try
class Foo5(val i: Int)
object Foo5Companion {
def apply(i: Int): Foo5 = new Foo5(i)
def apply(d: Double): Foo5 = new Foo5(d.toInt)
def applyTry(i: Int): Try[Foo5] = Try { apply(i) }
def applyTry(d: Double): Try[Foo5] = Try { apply(d) }
}
Now let's do something simple with this class.
val ls_i: List[Int] = List(1,2,3)
val ls_foo: List[Foo5] = ls_i.map(Foo5Companion.apply)
If I've loaded the class def and companion object with :paste I get this error... to be clear, Foo5 has only been defined once in a new session. This is not an instance of the issue described here: "error: type mismatch" in Spark with same found and required datatypes
<console>:42: error: type mismatch;
found : List[Foo5]
required: List[Foo5]
val ls_foo: List[Foo5] = ls_i.map(Foo5Companion.apply)
BUT...
if i load the same defs in line-by-line (without using :paste)... it works fine...
ls_foo: List[Foo5] = List($iwC$$iwC$Foo5#66f1a93a, $iwC$$iwC$Foo5#39d53a3, $iwC$$iwC$Foo5#4dddf42f)
My question is... what's the difference? Why is :paste causing a problem and making the repl think Foo5 is ambiguous?
Edit: this bug was fixed in 2.11.9, so latest 2.11.12 works.
Edit: since you're on Spark 1.6, I supposed you're stuck on 2.10. That's OK, I just picked up Homer again the other day, and in a very old version (Lattimore, 1951).
It looks like an old bug with how the Spark shell is handling imports from history under -Yrepl-class-based.
With -Xprint:typer:
import scala.util.Try;
import $line3.$read.INSTANCE.$iw.$iw.Foo5;
private[this] val $line3$read: $line3.$read = $line3.$read.INSTANCE;
<stable> <accessor> def $line3$read: $line3.$read = $iw.this.$line3$read;
import $iw.this.$line3$read.$iw.$iw.Foo5Companion;
One import is via an aliased member, so the paths to the two Foo5 differ.
You can use :load instead of :paste in this case. Normally, you'd :paste companions.
This is no consolation, but it will be fixed when Spark upgrades to 2.12:
$ ~/scala-2.11.8/bin/scala -Yrepl-class-based
Welcome to Scala 2.11.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.
scala> :paste foo5.scala
Pasting file foo5.scala...
import scala.util.Try
defined class Foo5
defined object Foo5Companion
scala> val ls_i: List[Int] = List(1,2,3)
ls_i: List[Int] = List(1, 2, 3)
scala> val ls_foo: List[Foo5] = List(1,2,3).map(Foo5Companion.apply)
<console>:15: error: type mismatch;
found : List[Foo5]
required: List[Foo5]
val ls_foo: List[Foo5] = List(1,2,3).map(Foo5Companion.apply)
^
scala> :quit
$ scala -Yrepl-class-based
________ ___ / / ___
/ __/ __// _ | / / / _ |
__\ \/ /__/ __ |/ /__/ __ |
/____/\___/_/ |_/____/_/ | |
|/ version 2.12.6
scala> :paste foo5.scala
Pasting file foo5.scala...
import scala.util.Try
defined class Foo5
defined object Foo5Companion
scala> val ls_i: List[Int] = List(1,2,3)
ls_i: List[Int] = List(1, 2, 3)
scala> val ls_foo: List[Foo5] = List(1,2,3).map(Foo5Companion.apply)
ls_foo: List[Foo5] = List(Foo5#52354202, Foo5#6b1321b7, Foo5#342ee097)

Is there a way to flatten nested monads of different types?

I am not sure how to describe this problem, so I'll just show the type signatures.
I have an instance of the following:
val x:Future[F[Future[F[B]]]] = ???
And I want an instance of:
val y:Future[F[B]] = ???
F is a Monad, so I have the following methods:
def pure[A](a:A):F[A] = ???
def flatMap[A, B](fa:F[A], f:A => F[B]):F[B] = ???
def map[A, B](fa:F[A], f:A => B):F[B] = flatMap(fa, (a:A) => pure(f(a)))
I think the following should work, but it does not feel right:
x.flatMap { fWithFuture =>
val p = Promise[F[B]]
flatMap(fWithFuture, (futureF: Future[F[B]]) => {
p.completeWith(futureF)
pure(())
})
p.future
}
Is there a concept I'm missing?
A bit of background information. I am trying to define a function like this:
def flatMap[A, B](fa:Future[F[A]], f: A => Future[F[B]]):Future[F[B]] = ???
Maybe this is conceptually a weird thing. Any tips on useful abstractions are welcome.
As Rex Kerr notes above, you can often use a monad transformer to deal with a situation where you find yourself with alternating layers like this. For example, if F here is Option, you could use Scalaz 7.1's OptionT monad transformer to write your flatMap:
import scalaz._, Scalaz._
type F[A] = Option[A]
def flatMap[A, B](fa: Future[F[A]], f: A => Future[F[B]]): Future[F[B]] =
OptionT(fa).flatMap(f andThen OptionT.apply).run
OptionT[Future, A] here is a kind of wrapper for Future[Option[A]]. If your F is List, just replace OptionT with ListT and run with underlying (and so on).
The nice thing is that when you're working with OptionT[Future, A], for example, you can generally avoid ending up with Future[Option[Future[Option[A]]]] in the first placeā€”see my answer here for some more detailed discussion.
One drawback is that not all monads have transformers. For example, you can put Future at the bottom of the stack (as I've done above), but there's not really a useful way to define FutureT.
This may answer the "And I want an instance of:" part.
$ scala
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_05).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.concurrent.Future
import scala.concurrent.Future
scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global
scala> Future(List(Future(1),Future(2),Future(3))) // Future[F[Future[B]]]
res0: scala.concurrent.Future[List[scala.concurrent.Future[Int]]] = scala.concurrent.impl.Promise$DefaultPromise#41ab013
scala> res0.map(Future.sequence(_)) // transformed to Future[Future[F[B]]
res1: scala.concurrent.Future[scala.concurrent.Future[List[Int]]] = scala.concurrent.impl.Promise$DefaultPromise#26a4842b
scala> res1.flatMap(identity) // reduced to Future[F[B]]
res2: scala.concurrent.Future[List[Int]] = scala.concurrent.impl.Promise$DefaultPromise#4152d38d
Hopefully the below flatMap definition should give an idea of transforming the types :)
I replaced F with a List type for ease of understanding.
scala> def flatMap[A, B](fa:Future[List[A]], f: A => Future[List[B]]):Future[List[B]] = {
| val x: Future[List[Future[List[B]]]] = fa.map(_.map(f))
| val y: Future[Future[List[List[B]]]] = x.map(Future.sequence(_))
| val z: Future[Future[List[B]]] = y.map(_.map(_.flatten))
| z.flatMap(identity)
| }
flatMap: [A, B](fa: scala.concurrent.Future[List[A]], f: A => scala.concurrent.Future[List[B]])scala.concurrent.Future[List[B]]

Best practices for mixing in Scala concurrent.Map

The ScalaDoc says this about concurrentMap: "Deprecated (Since version 2.10.0) Use scala.collection.concurrent.Map instead." Unfortunately, the rest of the Scala docs has not been updated and still references concurrentMap.
I tried to mix in concurrent.Map into a HashMap, with the following results:
scala> val mmap = new mutable.HashMap[String, String] with collection.concurrent.Map[String, String]
<console>:16: error: object creation impossible, since:
it has 4 unimplemented members.
/** As seen from anonymous class $anon, the missing signatures are as follows.
* For convenience, these are usable as stub implementations.
*/
def putIfAbsent(k: String,v: String): Option[String] = ???
def remove(k: String,v: String): Boolean = ???
def replace(k: String,v: String): Option[String] = ???
def replace(k: String,oldvalue: String,newvalue: String): Boolean = ???
val mmap = new mutable.HashMap[String, String] with collection.concurrent.Map[String, String]
So we see that instead of a simple mixin, some methods must also be implemented. Is this the best way to use concurrent.Map, or is there a better way?
The scala.collection.concurrent.Map trait is not meant to be mixed-in with an existing mutable Scala Map to obtain a thread-safe version of the map instance. The SynchronizedMap mixin existed for this purpose before 2.11, but is now deprecated.
Currently, Scala has the scala.collection.concurrent.TrieMap implementation for the scala.collection.concurrent.Map interface, but can wrap Java classes as well.
The scala.collection.concurrent.Map, in versions prior to 2.10 known as scala.collection.mutable.ConcurrentMap, interface is used when you:
want to implement your own concurrent, thread-safe Map from scratch
want to wrap an existing Java concurrent map implementation:
E.g:
import scala.collection._
import scala.collection.convert.decorateAsScala._
import java.util.concurrent.ConcurrentHashMap
val map: concurrent.Map[String, String] = new ConcurrentHashMap().asScala
want to write generic code that works concurrent maps, and don't want to commit to a specific implementation:
E.g.:
import scala.collection._
def foo(map: concurrent.Map[String, String]) = map.putIfAbsent("", "")
foo(new concurrent.TrieMap)
foo(new java.util.concurrent.ConcurrentSkipListMap().asScala)
you could implement your own wrapper around a single-threaded mutable map implementation by using synchronized (but you would need to ensure that your program is accessing the mutable map only through this wrapper and never directly).
E.g.:
class MySynchronizedMap[K, V](private val underlying: mutable.Map[K, V])
extends concurrent.Map[K, V] {
private val monitor = new AnyRef
def putIfAbsent(k: K,v: V): Option[String] = monitor.synchronized {
underlying.get(k) match {
case s: Some[V] => s
case None =>
underlying(k) = v
None
}
}
def remove(k: K, v: V): Boolean = monitor.synchronized {
underlying.get(k) match {
case Some(v0) if v == v0 => underlying.remove(k); true
case None => false
}
}
// etc.
}
Unless you want to implement a concurrent mutable hash map yourself, you have to use scala.collection.concurrent.TrieMap.
Taken from this comment directly:
https://stackoverflow.com/a/49689669/7082628
In 2018, apparently you can just do this:
import java.util.concurrent.ConcurrentHashMap
val m: ConcurrentHashMap[String,MyClass] = new ConcurrentHashMap
By "simple mixin", perhaps you're asking if the trait can be used as a decorator as shown here for SynchronizedMap, and the answer is apparently not.
Implementations include TrieMap and the wrapper for Java's ConcurrentMap (of which there are two implementations). (Java also offers ConcurrentSkipListSet as Set.)
Also see this roll-your-own question.
They have you covered on the conversion side of things, if that's what you were used to:
scala> import java.util.concurrent._
import java.util.concurrent._
scala> import collection.JavaConverters._
import collection.JavaConverters._
scala> val m = new ConcurrentHashMap[String, Int]
m: java.util.concurrent.ConcurrentHashMap[String,Int] = {}
scala> val mm = m.asScala
mm: scala.collection.concurrent.Map[String,Int] = Map()
scala> mm.replace("five",5)
res0: Option[Int] = None
scala> mm.getClass
res1: Class[_ <: scala.collection.concurrent.Map[String,Int]] = class scala.collection.convert.Wrappers$JConcurrentMapWrapper
Update for 2021 and Scala 2.13:
You need to use different implicit conversion when wrapping the Java concurrent map implementation:
import java.util.concurrent.ConcurrentHashMap
import scala.collection.concurrent
import scala.jdk.CollectionConverters._
val map: concurrent.Map[String, String] = new ConcurrentHashMap().asScala

Getting type information inside scala repl via IMain

Intent
I am trying to add support for :kind command to scala repl. Thanks to Eugene Burmako, I was able to get a working prototype. Though it only works with fully qualified names and fails to resolve imported names.
I am now trying to use IMain.exprTyper to do the job, as it is aware of types, imported into the repl. But there is a problem. Everything I've tried returns a ClassInfoType like the following (displayed with showRaw):
ClassInfoType(List(TypeRef(TypeRef(TypeRef(TypeRef(NoPrefix(), package <root>, List()), package java, List()), package lang, List()), class Object, List()), TypeRef(TypeRef(TypeRef(NoPrefix(), package <root>, List()), package scala, List()), trait Serializable, List())), Scope{
def <init>(): Option.type;
implicit def option2Iterable(xo: Option): Iterable;
def apply(x: Object): Option;
def empty(): Option;
private def readResolve(): Object
}, object Option)
While the working implementation returns specific Type:
PolyType(List(TypeName("A")), ClassInfoType(List(TypeRef(ThisType(scala), TypeName("AnyRef"), List()), TypeRef(ThisType(scala), scala.Product, List()), TypeRef(ThisType(scala), scala.Serializable, List())), Scope(nme.CONSTRUCTOR, TermName("isEmpty"), TermName("isDefined"), TermName("get"), TermName("getOrElse"), TermName("orNull"), TermName("map"), TermName("fold"), TermName("flatMap"), TermName("flatten"), TermName("filter"), TermName("filterNot"), TermName("nonEmpty"), TermName("withFilter"), TypeName("WithFilter"), TermName("contains"), TermName("exists"), TermName("forall"), TermName("foreach"), TermName("collect"), TermName("orElse"), TermName("iterator"), TermName("toList"), TermName("toRight"), TermName("toLeft")), scala.Option))
Question
I feel like I'm really close. Here's a playground, you can use to try everything yourself:
Welcome to Scala version 2.11.0-20130328-093148-47645c7e7e (OpenJDK 64-Bit Server VM, Java 1.7.0_17).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> import scala.tools.nsc.interpreter.IMain
import scala.tools.nsc.interpreter.IMain
scala> val mirror = runtimeMirror(getClass.getClassLoader) // Working approach
mirror: reflect.runtime.universe.Mirror = JavaMirror with scala.tools.nsc.interpreter.IMain$TranslatingClassLoader#3d34ec98 of type class scala.tools.nsc.interpreter.IMain$TranslatingClassLoader with classpath [<unknown>] and parent being scala.tools.nsc.util.ScalaClassLoader$URLClassLoader#5d990e8c of type class scala.tools.nsc.util.ScalaClassLoader$URLClassLoader with classpath [file:/usr/lib/jvm/java-7-openjdk/jre/lib/resources.jar,file:/usr/lib/jvm/java-7-openjdk/jre/lib/rt.jar,file:/usr/lib/jvm/java-7-openjdk/jre/lib/jsse.jar,file:/usr/lib/jvm/java-7-openjdk/jre/lib/jce.jar,file:/usr/lib/jvm/java-7-openjdk/jre/lib/charsets.jar,file:/usr/lib/jvm/java-7-openjdk/jre/lib/rhino.jar,file:/home/folone/workspace/scala-myfork/build/pack/lib/jline.jar,file:/home/folone/workspace/scala-myfork...
scala> val typer = new IMain().exprTyper // Not working approach
typer: scala.tools.nsc.interpreter.IMain#exprTyper.type = scala.tools.nsc.interpreter.IMain$exprTyper$#68c181f0
scala> val expr = "scala.Option"
expr: String = scala.Option
scala> showRaw(mirror.staticClass(expr).toType.typeSymbol.typeSignature) // Correct signature
res6: String = PolyType(List(TypeName("A")), ClassInfoType(List(TypeRef(ThisType(scala), TypeName("AnyRef"), List()), TypeRef(ThisType(scala), scala.Product, List()), TypeRef(ThisType(scala), scala.Serializable, List())), Scope(nme.CONSTRUCTOR, TermName("isEmpty"), TermName("isDefined"), TermName("get"), TermName("getOrElse"), TermName("orNull"), TermName("map"), TermName("fold"), TermName("flatMap"), TermName("flatten"), TermName("filter"), TermName("filterNot"), TermName("nonEmpty"), TermName("withFilter"), TypeName("WithFilter"), TermName("contains"), TermName("exists"), TermName("forall"), TermName("foreach"), TermName("collect"), TermName("orElse"), TermName("iterator"), TermName("toList"), TermName("toRight"), TermName("toLeft")), scala.Option))
scala> showRaw(typer.typeOfExpression(expr).typeSymbol.typeSignature) // Wrong signature
res7: String =
ClassInfoType(List(TypeRef(TypeRef(TypeRef(TypeRef(NoPrefix(), package <root>, List()), package java, List()), package lang, List()), class Object, List()), TypeRef(TypeRef(TypeRef(NoPrefix(), package <root>, List()), package scala, List()), trait Serializable, List())), Scope{
def <init>(): Option.type;
implicit def option2Iterable(xo: Option): Iterable;
def apply(x: Object): Option;
def empty(): Option;
private def readResolve(): Object
}, object Option)
How do I transform ClassInfoType into a valid Type containing the needed info? Alternatively, how do I get the Type using IMain in the first place?
How about this? I'm using power mode which gives you access to the global from the currently running REPL, which is a shade more convenient than creating a new IMain.
scala> :power
Already in power mode.
scala> val g = global
g: $r.intp.global.type = <global>
scala> val context = g.analyzer.rootContext(NoCompilationUnit)
context: g.analyzer.Context = Context(<root>#EmptyTree unit=NoCompilationUnit scope=997093283 errors=false, reportErrors=true, throwErrors=false)
// aware imports of scala._, etc.
scala> val sym = context.lookupSymbol("Option": TypeName, _ => true).symbol
sym: g.analyzer.global.Symbol = class Option
scala> sym.tpeHK.typeParams
res21: List[g.analyzer.global.Symbol] = List(type A)
See also:
scala> intp.symbolOfType("Foo")
res26: $r.intp.global.Symbol = class Foo
But I'm not sure how to get at previously imported symbols:
scala> object Bar { class Bop }
defined object Bar
scala> import Bar.Bop
import Bar.Bop
scala> intp.symbolOfType("Bop")
res27: $r.intp.global.Symbol = <none>
Edit:
The reason OP's getting ClassInfoType instead of PolyType is due to phase. To get the same result as the power mode's global, one must set the phase to typer. To quote #retronym's explanation from REPL: intp.global vs "global" available in :power mode:
scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'. **
^
`---- this part is relevant
Symbols have an list of types (aka info-s), indexed by compiler phase. (aka TypeHistory). Many compiler phases install InfoTransformers to morph the type. See src/compiler/scala/tools/nsc/transform/InfoTransform.scala for some documentation.
To inspect the type as-at particular phase, you can use methods like 'exitingTyper`.
scala> exitingPostErasure($intp.global.rootMirror.staticClass("scala.Option").typeSignature).getClass
res6: Class[_ <: $intp.global.Type] = class scala.reflect.internal.Types$ClassInfoType
scala> exitingTyper($intp.global.rootMirror.staticClass("scala.Option").typeSignature).getClass
res7: Class[_ <: $intp.global.Type] = class scala.reflect.internal.Types$PolyType
Or, a little more conveniently in :power mode:
scala> :phase typer
Active phase is now: Typer
scala> global.rootMirror.staticClass("scala.Option").typeSignature.getClass
res16: Class[_ <: $r.global.Type] = class scala.reflect.internal.Types$PolyType
scala> :phase cleanup
Active phase is now: Cleanup
scala> global.rootMirror.staticClass("scala.Option").typeSignature.getClass
res17: Class[_ <: $r.global.Type] = class scala.reflect.internal.Types$ClassInfoType
You should use the mirror from the IMain's global:
scala> val imain = new IMain()
imain: scala.tools.nsc.interpreter.IMain = scala
scala> val mirror = imain.global.rootMirror
mirror: imain.global.Mirror = compiler mirror

Scala short and type safe cast operator

I don't like Scala isInstanceOf and asInstanceOf methods - they are long and asInstanceOf can throw exception so we need to use it in couple. Better way is to use pattern matching: Scala: How do I cast a variable? but for really simple operations it can be relatively long too. In C# we have 'is' and 'as' operators so I wanted to implement implicit definition with this in Scala. My code look like this:
scala> class TypeCast(x:Any){
| def is[T](t:Class[T]) = t.isInstance(x)
| def as[T](t:Class[T]):Option[T] = if(t.isInstance(x)) Option(t.cast(x)) else None
| }
defined class TypeCast
scala> implicit def TypeCastID(x:Any)=new TypeCast(x)
TypeCastID: (x: Any)TypeCast
scala> 123 as classOf[String]
res14: Option[String] = None
scala> "asd" as classOf[String]
res15: Option[String] = Some(asd)
It has one advantage - implement null-object pattern but also have disadvantages:
need to use classOf[T] operator - it's too long
overhead connected with implicit def for such simple operation
so there is no practical reason to use it.
I would like to know is there any way to implement this without need to use classOf[T]?
Well you could shorten it down inside the def you made in the TypeCast class. So instead of feeding it a parameter you could just rely on the type. This would shorten it down a lot. As an example this could look something like:
class TypeCast(x : Any) {
def is[T : Manifest] = manifest.erasure.isInstance(x)
def as[T : Manifest] : Option[T] = if (manifest.erasure.isInstance(x)) Some(x.asInstanceOf[T]) else None
}
Future calls could look like:
scala> 123.as[String]
res0: Option[String] = Some(123)
scala> class A; class B extends A
defined class A
defined class B
scala> new B
res1: B
scala> res1.is[Int]
res2: Boolean = false
scala> res1.as[Int]
res3: Option[Int] = None
Update: I added Manifests to avoid type-check errors