Consider the following setup:
trait Foo[A]
object Foo extends Priority2
trait Priority0 {
implicit def foo1: Foo[Int] = new Foo[Int] {}
}
trait Priority1 extends Priority0 {
implicit def foo2: Foo[Boolean] = new Foo[Boolean] {}
}
trait Priority2 extends Priority1 {
implicit def foo3: Foo[Double] = new Foo[Double] {}
}
Now, in a REPL (having loaded the above code up), I can do the following:
scala> def implicitlyFoo[A](implicit foo: Foo[A]) = foo
implicitlyFoo: [A](implicit foo: Foo[A])Foo[A]
scala> implicitlyFoo
res1: Foo[Double] = Priority2$$anon$3#79703b86
Is there a way to encode with some typelevel magic that I want to skip over the instances with A =:= Double, but still let type inference figure out what A is?
I do not want to shadow foo3. This is an MVCE: in my real case, foo3 is a def with other implicit arguments (and may play an indirect role in deriving other Foo's).
I've tried =:!= from shapeless but to no avail:
scala> import shapeless._
import shapeless._
scala> def implicitlyFoo2[A](implicit foo: Foo[A], ev: A =:!= Double) = foo
implicitlyFoo2: [A](implicit foo: Foo[A], implicit ev: A =:!= Double)Foo[A]
scala> implicitlyFoo2
<console>:16: error: ambiguous implicit values:
both method neqAmbig1 in package shapeless of type [A]=> A =:!= A
and method neqAmbig2 in package shapeless of type [A]=> A =:!= A
match expected type Double =:!= Double
implicitlyFoo2
^
Dirty hack is to downcast macro context to its implemenation and use compiler internals.
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
trait Foo[A] {
def say: String
}
trait Priority0 {
implicit def foo1: Foo[Int] = new Foo[Int] {
override def say: String = "int"
}
}
trait Priority1 extends Priority0 {
implicit def foo2: Foo[Boolean] = new Foo[Boolean] {
override def say: String = "bool"
}
}
trait Priority2 extends Priority1 {
implicit def foo3: Foo[Double] = new Foo[Double] {
override def say: String = "double"
}
}
object Foo extends Priority2
def materializeSecondFoo[A]: Foo[A] = macro impl
def impl(c: whitebox.Context): c.Tree = {
import c.universe._
val context = c.asInstanceOf[reflect.macros.runtime.Context]
val global: context.universe.type = context.universe
val analyzer: global.analyzer.type = global.analyzer
var infos = List[analyzer.ImplicitInfo]()
new analyzer.ImplicitSearch(
tree = EmptyTree.asInstanceOf[global.Tree],
pt = typeOf[Foo[_]].asInstanceOf[global.Type],
isView = false,
context0 = global.typer.context.makeImplicit(reportAmbiguousErrors = false),
pos0 = c.enclosingPosition.asInstanceOf[global.Position]
) {
override def searchImplicit(
implicitInfoss: List[List[analyzer.ImplicitInfo]],
isLocalToCallsite: Boolean
): analyzer.SearchResult = {
val implicitInfos = implicitInfoss.flatten
if (implicitInfos.nonEmpty) {
infos = implicitInfos
}
super.searchImplicit(implicitInfoss, isLocalToCallsite)
}
}.bestImplicit
val secondBest = infos.tail.head
global.gen.mkAttributedRef(secondBest.pre, secondBest.sym).asInstanceOf[Tree]
}
materializeSecondFoo.say // bool
Tested in 2.12.8. Motivated by shapeless.Cached.
In 2.13.0 materializeSecondFoo.say should be replaced with
val m = materializeSecondFoo
m.say
The latter is still working in 2.13.10.
Scala 3 implementation:
import scala.quoted.{Quotes, Type, Expr, quotes}
import dotty.tools.dotc.typer.{Implicits => dottyImplicits}
transparent inline def materializeSecondFoo: Foo[_] = ${impl}
def impl(using Quotes): Expr[Foo[_]] = {
import quotes.reflect.*
given c: dotty.tools.dotc.core.Contexts.Context =
quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
val typer = c.typer
val search = new typer.ImplicitSearch(
TypeRepr.of[Foo[_]].asInstanceOf[dotty.tools.dotc.core.Types.Type],
dotty.tools.dotc.ast.tpd.EmptyTree,
Position.ofMacroExpansion.asInstanceOf[dotty.tools.dotc.util.SourcePosition].span
)
def eligible(contextual: Boolean): List[dottyImplicits.Candidate] =
if contextual then
if c.gadt.isNarrowing then
dotty.tools.dotc.core.Contexts.withoutMode(dotty.tools.dotc.core.Mode.ImplicitsEnabled) {
c.implicits.uncachedEligible(search.wildProto)
}
else c.implicits.eligible(search.wildProto)
else search.implicitScope(search.wildProto).eligible
def implicits(contextual: Boolean): List[dottyImplicits.SearchResult] =
eligible(contextual).map(search.tryImplicit(_, contextual))
val contextualImplicits = implicits(true)
val nonContextualImplicits = implicits(false)
val contextualSymbols = contextualImplicits.map(_.tree.symbol)
val filteredNonContextual = nonContextualImplicits.filterNot(sr => contextualSymbols.contains(sr.tree.symbol))
val successes = (contextualImplicits ++ filteredNonContextual).collect {
case success: dottyImplicits.SearchSuccess => success.tree.asInstanceOf[ImplicitSearchSuccess].tree
}
successes.tail.head.asExprOf[Foo[_]]
}
materializeSecondFoo.say // bool
val foo = materializeSecondFoo
foo: Foo[Boolean] // compiles
Scala 3.2.0.
Related
For a macro implementation I'm stacking types and field names with this boilerplate code:
trait Init
trait A_ {
type Push[T]
val int: Push[Int] = ???
val str: Push[String] = ???
}
object A extends A_ {
type Push[T] = Init with B[Init with T]
}
trait B_ {
type Push[T]
val int: Push[Int] = ???
val str: Push[String] = ???
}
trait B[Stack] extends B_ {
type Push[T] = C[Stack with T]
}
trait C[Stack] // etc..
val x: Init with B[Init with Int] = A.int
val y: C[Init with Int with String] = A.int.str
val z: C[Init with String with Int] = A.str.int
// etc..
This works fine. The user can write A.int.str or A.str.int and the macro can easily extract the involved types and fields.
Since many more types and variations are involved, I'd like to replace A_ and B_ with a single base class if possible to avoid the redundancy. So I tried this:
trait AB {
type Push[T]
val int: Push[Int] = ???
val str: Push[String] = ???
}
object A extends AB {
type Push[T] = Init with B[Init with T]
}
trait B[Stack] extends AB {
type Push[T] = C[Stack with T]
}
trait C[Stack] // etc..
val x: Init with B[Init with Int] = A.int
val y: Init with B[Init with String] = A.int.str
But as you can see, y lost the Int type on the way.
When calling str, the Push type used is still the one defined in A. I had hoped for the returned Init with B[Init with T] to return a new instance of B[Stack] extends AB using its Push type leading to C and so on.
A self reference attempt fails too:
trait AB { self: {type Push[T]} =>
val int: Push[Int] = ???
val str: Push[String] = ???
}
object A extends AB {
type Push[T] = Init with B[Init with T]
}
trait B[Stack] extends AB {
type Push[T] = C[Stack with T]
}
trait C[Stack] // etc..
val x: A.Push[Int] = A.int
val y: A.Push[String] = A.int.str
I'm obviously missing something and another self reference implementation doesn't work either:
trait Pusher {
type Push[T] = _
}
trait AB { self: Pusher =>
val int: Push[Int] = ???
val str: Push[String] = ???
}
object A extends AB with Pusher {
override type Push[T] = Init with B[Init with T]
}
trait B[Stack] extends AB with Pusher {
override type Push[T] = C[Stack with T]
}
trait C[Stack] // etc..
val x: A.Push[Int] = A.int
val y: A.Push[String] = A.int.str
Is there a way to have a single implementation of int/str and get val y: C[Init with Int with String] = A.int.str (or something similar) that I can use to extract the types in the macro implementation?
UPDATE:
#AlinGabrielArhip is right in his comment that the type information is actually carried on and compiles even though the types of the second approaches with a single base class are not inferred correctly which show with red lines in IntelliJ.
To give more context to my question, here are the two approaches that you can test in the REPL:
Start scala with `scala -Yrepl-class-based:false´
Paste and run the following 4 sections of code one at a time:
object SetupA {
import scala.reflect.macros.blackbox
import scala.language.experimental.macros
trait Init
trait Name {
val name: String = ""
}
trait Age {
val age: Int = 0
}
trait Person0_base {
type Push[T]
val name: Push[Name] = ???
val age : Push[Age] = ???
}
object PersonA extends Person0_base {
type Push[T] = Init with Person1[Init with T]
}
trait Person1_base {
type Push[T]
val name: Push[Name] = ???
val age : Push[Age] = ???
}
trait Person1[Stack] extends Person1_base {
type Push[T] = Person2[Stack with T]
}
trait Person2[Stack]
def impl[T](c: blackbox.Context)(person: c.Tree): c.Tree = {
import c.universe._
q"""
new Init with Name with Age {
// Data fetched from db..
override val name = "Ben"
override val age = 42
}
"""
}
def macroA[T](person: Person2[T]): T = macro impl[T]
}
import SetupA._
// Correctly inferred as `Init with Name with Age`
val personA = macroA(PersonA.name.age)
personA.name == "Ben"
personA.age == 42
object SetupB {
import scala.reflect.macros.blackbox
import scala.language.experimental.macros
trait Init
trait Name {
val name: String = ""
}
trait Age {
val age: Int = 0
}
trait Person_base {
type Push[T]
val name: Push[Name] = ???
val age : Push[Age] = ???
}
object PersonB extends Person_base {
type Push[T] = Init with Person1[Init with T]
}
trait Person1[Stack] extends Person_base {
type Push[T] = Person2[Stack with T]
}
trait Person2[Stack] {
val get: Stack = ???
}
def impl[T](c: blackbox.Context)(person: c.Tree): c.Tree = {
import c.universe._
q"""
new Init with Name with Age {
// Data fetched from db..
override val name = "Ben"
override val age = 42
}
"""
}
def macroB[T](person: Person2[T]): T = macro impl[T]
}
import SetupB._
// Incorrectly inferred as `PersonB.Push[Age]` - but still compiles!
val personB = macroB(PersonB.name.age)
personB.name == "Ben"
personB.age == 42
Is there a workaround to get correct type inference in this case?
Had made this more complicated than necessary. By simply supplying Stack and Push as type parameters to the base class, type inference works as expected:
object Test {
import scala.reflect.macros.blackbox
import scala.language.experimental.macros
trait Init
trait Name {
val name: String = ""
}
trait Age {
val age: Int = 0
}
trait PersonBase[Stack, Push[_]] {
val name: Push[Stack with Name] = ???
val age : Push[Stack with Age] = ???
}
object Person extends PersonBase[Init, Person1]
trait Person1[Stack] extends PersonBase[Stack, Person2]
trait Person2[Stack] extends PersonBase[Stack, Person3]
trait Person3[Stack]
def impl[T](c: blackbox.Context)(person: c.Tree): c.Tree = {
import c.universe._
q"""
new Init with Name with Age {
// Data fetched from db...
override val name = "Ben"
override val age = 42
}
"""
}
def m[T](person: Person2[T]): T = macro impl[T]
}
import Test._
val person = m(Person.name.age)
person.name == "Ben"
person.age == 42
UPDATE:
Although this answer solves stacking types, it actually doesn't solve the broader problem that the initial use of abstract types was intended for. So I marked it as unanswered.
The problem behind is that the sub classes have varying type parameter arities. So they can't simply be passed as type parameters to a single base class (at least I haven't found a way to do that).
As #AlinGabrielArhip helped me find out, it's a type inference problem and I have submitted this bug report to IntelliJ.
So with a little better understanding of the problem, here's an example of what I want to get working which seems dependent on IntelliJ to fix the type inference bug:
scala -Yrepl-class-based:false
object Setup {
import scala.reflect.macros.blackbox
import scala.language.experimental.macros
trait Name {
val name: String = ""
}
trait Age {
val age: Int = -1
}
trait PersonAttributes {
type Next[Attr, Tpe]
val name: Next[Name, String] = ???
val age : Next[Age, Int] = ???
}
trait Person0[Obj] extends PersonAttributes {
type Next[Attr, Tpe] = Person1[Obj with Attr, Tpe]
}
trait Person1[Obj, A] extends PersonAttributes {
type Next[Attr, Tpe] = Person2[Obj with Attr, A, Tpe]
}
trait Person2[Obj, A, B] // etc..
def objImpl[Obj, A, B](c: blackbox.Context)(person2: c.Tree): c.Tree = {
import c.universe._
q"""
new Init with Name with Age {
// Data fetched from db..
override val name = "Ben"
override val age = 42
}
"""
}
def obj[Obj, A, B](person2: Person2[Obj, A, B]): Obj = macro objImpl[Obj, A, B]
}
import Setup._
trait Init
object Person extends Person0[Init]
// Incorrectly inferred as `PersonB.Push[Age]` - but still compiles!
// Should be inferred as: `Person2[Init with String with Int]`
val person = obj(Person.name.age)
person.name == "Ben"
person.age == 42
The initial code example in my question actually only works because A defines
type Push[T] = Init with B[Init with T]
instead of
type Push[T] = B[Init with T]
The added Init with just helped the type inference. But this "trick" doesn't work with the single base class approach.
So now, I simply hope for IntelliJ to fix the bug.
I'm trying to generate instances of case class using shapeless
This works for generating instances of Foo
case class Foo(x: Int, y: String)
class Context {
val random = new Random()
}
def genInt(context: Context): Int = {
context.random.nextInt()
}
def genString(context: Context): String = {
context.random.nextString(16)
}
object ClassesToGenerators extends Poly1 {
implicit def caseInt = at[Class[Int]](_ => genInt(_))
implicit def caseString = at[Class[String]](_ => genString(_))
}
val gen = Generic[Foo]
val context = new Context()
val classes = classOf[Int] :: classOf[String] :: HNil // can't figure out how to get this hlist programmatically
val generators = classes.map(ClassesToGenerators)
gen.from(generators.zipApply(generators.mapConst(context)))
However, I'm aiming to write something reusable like
def newInstance[T] -> T:
???
which could generate instances of any case classes that takes only int and string parameters.
As mentioned in the code snippet, I'm stuck at getting a hlist of case class attribute types i.e. would like to convert case class Foo(x: Int, y: String) to classOf[Int] :: classOf[String] :: HNil. Any other approaches to this problem are also very appreciated but I'm not looking for a generic way of generating random instances of cases classes (as my use-case is different and used random generator just as an example)
IMHO, Shapeless is better used when you forget about all the fancy stuff and just focus on simple typeclass derivation using HList like this:
import shapeless.{Generic, HList, HNil, :: => :!:}
import scala.util.Random
trait Context {
def random: Random
}
object Context {
object implicits {
implicit final val global: Context = new Context {
override final val random: Random = new Random()
}
}
}
trait Generator[A] {
def generate(context: Context): A
}
object Generator {
final def apply[A](implicit ev: Generator[A]): ev.type = ev
final def generate[A](implicit ev: Generator[A], ctx: Context): A =
ev.generate(ctx)
implicit final val IntGenerator: Generator[Int] =
new Generator[Int] {
override def generate(context: Context): Int =
context.random.nextInt()
}
implicit final val StringGenerator: Generator[String] =
new Generator[String] {
override def generate(context: Context): String =
context.random.nextString(16)
}
implicit final def auto[P <: Product](implicit ev: GeneratorGen[P]): Generator[P] = ev
}
sealed trait GeneratorRepr[R <: HList] extends Generator[R]
object GeneratorRepr {
implicit final val HNilGeneratorRepr: GeneratorRepr[HNil] =
new GeneratorRepr[HNil] {
override def generate(context: Context): HNil =
HNil
}
implicit final def HConsGeneratorRepr[E, T <: HList](
implicit ev: Generator[E], tail: GeneratorRepr[T]
): GeneratorRepr[E :!: T] =
new GeneratorRepr[E :!: T] {
override def generate(context: Context): E :!: T =
ev.generate(context) :: tail.generate(context)
}
}
sealed trait GeneratorGen[P <: Product] extends Generator[P]
object GeneratorGen {
implicit final def instance[P <: Product, R <: HList](
implicit gen: Generic.Aux[P, R], ev: GeneratorRepr[R]
): GeneratorGen[P] = new GeneratorGen[P] {
override def generate(context: Context): P =
gen.from(ev.generate(context))
}
}
Which can be used like this:
import Context.implicits.global
final case class Foo(x: Int, y: String)
val result = Generator.generate[Foo]
// result: Foo = Foo(-2127375055, "鞰Ϗƨ沺㗝䚮Ⴍ욏ꖱꬮӝ闉믃雦峷")
You can see the code running here.
Using built-in Shapeless type classes you can do
import shapeless.ops.hlist.FillWith
import shapeless.{Generic, HList, Poly0}
val context = new Context()
object ClassesToGenerators extends Poly0 {
implicit val caseInt = at[Int](genInt(context))
implicit val caseString = at[String](genString(context))
}
def newInstance[A] = new PartiallyApplied[A]
class PartiallyApplied[A] {
def apply[L <: HList]()(implicit
generic: Generic.Aux[A, L],
fillWith: FillWith[ClassesToGenerators.type, L]
): A = generic.from(fillWith())
}
newInstance[Foo]() // Foo(2018031886,⮐掐禃惌ᰧ佨妞ዤࠒ훿柲籐妭蝱⻤)
I have the following code, that I can not figure out, what the difference:
implicit def boxPrintable[A](implicit p: Printable[A]) =
p.contramap[Box[A]](_.value)
implicit val stringPrintable: Printable[String] =
new Printable[String] {
def format(value: String): String =
"Foo " |+| value |+| " Too"
}
Both are the implementation for types. The question is, when to use def and when to use val?
The whole code:
package com.sweetsoft
import cats.instances.string._
import cats.syntax.semigroup._
import cats.Contravariant
import cats.Show
import cats.instances.string._
final case class Box[A](value: A)
trait Printable[A] {
self =>
def format(value: A): String
def contramap[B](func: B => A): Printable[B] =
new Printable[B] {
override def format(value: B): String = self.format(func(value))
}
}
object Main {
val showString = Show[String]
implicit def boxPrintable[A](implicit p: Printable[A]) =
p.contramap[Box[A]](_.value)
implicit val stringPrintable: Printable[String] =
new Printable[String] {
def format(value: String): String =
"Foo " |+| value |+| " Too"
}
implicit val booleanPrintable: Printable[Boolean] =
new Printable[Boolean] {
def format(value: Boolean): String =
if (value) "yes" else "no"
}
def main(args: Array[String]): Unit = {
println(format("Hello"))
//println(format(Box("hello world")))
}
def format[A](value: A)(implicit p: Printable[A]): String =
p.format(value)
}
Your boxPrintable takes a type parameter A and a value argument of type Printable[A], so it must be a def.
String is one specific type, so stringPrintable takes no parameters at all, it's just a constant, so you can define it to be a val.
Nothing more to it.
def is evaluated on each request.
val is evaluated when the class is created.
In your example you need a def as soon it has parameters, which are not possible with vals.
I want to do something like this:
def fold[C[A]](implicit ev: Foldable[A]): A
I am getting not found: type A
I know, I can do this instead:
def fold[C[_], A: Foldable]: A
But, I would rather invoke as fold[List[Int]] than fold[List, Int]
I played a bit with it and came up with a helper type class:
trait Helper[M[_], CA] {
type C[_]
type A
implicit def ma: M[A]
}
object Helper {
implicit def instance[M0[_], C0[_], A0](implicit ma0: M0[A0]) = new Helper[M0, C0[A0]] {
type C[X] = C0[X]
type A = A0
val ma: M0[A0] = ma0
}
}
I know the names are pretty generic, I'd suggest finding more meaningful names.
Now instead of requiring an implicit of type Foldable[A] you require instead an implicit of Helper[Foldable, CA] where CA is the type that must match against List[Int] in your example:
def fold[CA](implicit helper: Helper[Foldable, CA]): helper.A
As an example:
def fold[CA](implicit helper: Helper[Foldable, CA]): helper.A = {
import helper._
println(implicitly[Foldable[A]])
null.asInstanceOf[A]
}
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Foldable[A](name: String)
implicit val stringFoldable = Foldable[String]("String")
implicit val intFoldable = Foldable[Int]("Int")
implicit val floatFoldable = Foldable[Float]("Float")
def fold[CA](implicit helper: Helper[Foldable, CA]): helper.A = {
import helper._
println(implicitly[Foldable[A]])
null.asInstanceOf[A]
}
// Exiting paste mode, now interpreting.
defined class Foldable
stringFoldable: Foldable[String] = Foldable(String)
intFoldable: Foldable[Int] = Foldable(Int)
floatFoldable: Foldable[Float] = Foldable(Float)
fold: [CA](implicit helper: Helper[Foldable,CA])helper.A
scala> fold[List[String]]
Foldable(String)
res0: String = null
scala> fold[List[Int]]
Foldable(Int)
res1: Int = 0
scala> fold[List[Float]]
Foldable(Float)
res2: Float = 0.0
Here's what I came up with:
trait Foo[T, A]
implicit def makeFoo[A, M[_]] = new Foo[M[A], A] {}
class Helper[T] {
def apply[A]()(implicit ev: Foo[T, A]) = ev
}
def bar[T] = new Helper[T]
bar[List[Int]]()
//Foo[List[Int],Int] = $anon$1#7edf6563
The empty pair of parens might not be ideal if you really want an a no-arg method, but I can't see how to get around that at the moment.
I have an Operator abstract class:
abstract class Operator[T, U] {
def setParent(op: Operator[T, U]): Unit
def newOp(): Operator[Byte, String] = {
val newOperator = new NewOperator[Byte, String]
newOperator.setParent(this)
newOperator
}
}
and another NewOperator class
class NewOperator[T, U] extends Operator[T, U] {
var parent: Operator[T,U] = null
def setParent(op: Operator[T, U]): Unit = {this.parent = op}
}
Now, in the second line in newOp() method in class Operator, I get an error
newOperator.setParent(this)
^
which says: Type mismatch: expected: Operator[Byte, String], actual: Operator[T, U].
Is the only way to resolve this, is to add .instanceOf[Operator[Byte, String]] to this?
newOperator.setParent(this.instanceOf[Operator[Byte, String]])
why not make the method newOp generic?
abstract class Operator[T, U] {
def setParent(op: Operator[T, U]): Unit
def newOp(): Operator[T, U] = {
val newOperator = new NewOperator[T, U]
newOperator.setParent(this)
newOperator
}
}
class NewOperator[T, U] extends Operator[T, U] {
var parent: Operator[T,U] = null
def setParent(op: Operator[T, U]): Unit = {this.parent = op}
}
val op = new NewOperator[Byte, String]().newOp
Your current solution restricts all your Operator[Byte, String] to have Operator[Byte, String] parent type (and you will found it only in runtime if you use asInstanceOf).
In general case if parent/child generic type may be different use:
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Operator[T, U, P <: Operator[_, _, _]] {
var parent: P = null.asInstanceOf[P]
def setParent(op: P): Unit = {this.parent = op}
def newOp[TT, UU]() = {
val newOperator = new Operator[TT, UU, Operator[T, U, P]]()
newOperator.setParent(this)
newOperator
}
}
// Exiting paste mode, now interpreting.
defined class Operator
scala> new Operator[Byte, String, Null]
res19: Operator[Byte,String,Null] = Operator#5470e2f4
scala> res19.newOp[Int, String]
res20: Operator[Int,String,Operator[Byte,String,Null]] = Operator#729c1e43
scala> res20.parent
res21: Operator[Byte,String,Null] = Operator#5470e2f4
You may move newOp to some subclass and make Operator a trait, if you need to have some specific state/methods for your Operator.
Or you can use type classes for operator specific operations:
scala> new Operator[Byte, String, Null]
res23: Operator[Byte,String,Null] = Operator#728b49e2
scala> implicit class ByteOperator(o: Operator[Byte, String, _]) {
| def hello = "hello" //here you can access some members of Operator
| }
defined class ByteOperator
scala> res23.hello
res24: String = hello
If you really need children having same generic as parents:
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Operator[T, U] {
var parent: Operator[T, U] = null
def newInstance: Operator[T, U] = new Operator[T, U]
def newOp: Operator[T, U] = {
val newOperator = newInstance
newOperator.setParent(this)
newOperator
}
def setParent(op: Operator[T, U]): Unit = {this.parent = op}
}
// Exiting paste mode, now interpreting.
defined class Operator
scala> new Operator[Byte, String]
res15: Operator[Byte,String] = Operator#4e6ea769
scala> res15.newOp
res16: Operator[Byte,String] = Operator#c774157
scala> res16.parent
res17: Operator[Byte,String] = Operator#4e6ea769
If you just need to model some AST (Abstract Syntax Tree), case classess may be a good solution:
trait Expression[T] {
def v: T
}
case class Value[T](v: T) extends Expression[T]
case class Plus[T1, T2](a: Expression[T1], b: Expression[T2])(implicit wrap: T1 => Arithmetic[T1, T2]) extends Expression[T1] {
def v = wrap(a.v) ++ b.v
}
abstract class Arithmetic[T1, T2](v: T1) {
def ++ (v: T2): T1
}
implicit class ArithmeticInt(val v: Int) extends Arithmetic[Int, Int](v) {
def ++ (v2: Int) = v + v2
}
implicit class ArithmeticIntDouble(val v: Int) extends Arithmetic[Int, Double](v) {
def ++ (v2: Double) = (v.toDouble + v2).toInt
}
scala> Plus(Value(5.0), Value(11.0)).v
res57: Value[Double] = Value(16.0)
scala> Plus(Value(5), Value(11.0)).v
res67: Int = 16
scala> Plus(Value(5), Value(6)).v
res68: Int = 11
scala> Plus(Value(5.0), Value(6)).v
<console>:60: error: No implicit view available from Double => Arithmetic[Double,Int].
Plus(Value(5.0), Value(6)).v
^
Instead of doing like this
newOperator.setParent(this) in abstract class operator you can do something like this
newOperator.setParent(newOperator)
This will solve your problem