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.
Related
I am a starter with Scala 2 Macros (before I switch to Dotty) who after trying out the shapeless type class derivation wanted to go one step beyond and write a macro that can generate a type class instances for any scala.Product without it.
(for the sake of example let's ignore nested recursive types, so my goal is flat case classes.)
My type class is an abstract class Coder[T] (e.g. trait with encode() / decode()).
So the generated code for:
case class Pojo(
s: String,
i: Int,
l: List[Int]
)
should be something like:
import com.github.fpopic.scalamacros.Pojo
import org.apache.beam.sdk.coders.Coder
import java.io.{ByteArrayInputStream, ByteArrayOutputStream}
import java.util
class PojoCoder extends Coder[Pojo] {
import com.github.fpopic.scalamacros.beam.DefMacroCoder.{
stringCoder,
intCoder,
listCoder
}
override def encode(value: Pojo, os: OutputStream): Unit = {
stringCoder.encode(value.s, os)
intCoder.encode(value.i, os)
listCoder(intCoder).encode(value.l, os)
}
override def decode(is: InputStream): Pojo = {
Pojo(
s = stringCoder.decode(is),
i = intCoder.decode(is),
l = listCoder(intCoder).decode(is)
)
}
override def getCoderArguments: util.List[_ <: Coder[_]] = {
Collections.emptyList()
}
override def verifyDeterministic(): Unit = ()
}
(removed fully specified class names to improve readability)
In the macro I try to:
inspect the weakTypeOf[P]
iterate over each case class constructor field (of type F)
implicitly find their typeclass Coder[F] instance and add it to the tree
and append their encode() and decode() expression to the tree that contributes to the final Coder[P] methods.
def materializeProductCoder[P: c.WeakTypeTag](c: blackbox.Context): c.Expr[Coder[P]] = {
import c.universe._
val tpe = c.weakTypeOf[P]
val helper = new MacrosHelper[c.type](c)
val expressions =
helper.getPrimaryConstructorMembers(tpe).map { field =>
val fieldTerm = field.asTerm.name // e.g. value.s (for now just s)
val fieldType = field.typeSignature.finalResultType // e.g. String
val fieldCoderName = c.freshName(TermName("coder")) // e.g. give friendly name coder$...
val fieldCoderInstance = // e.g. finds instance of Coder[String]
c.typecheck(
tree = q"""_root_.scala.Predef.implicitly[org.apache.beam.sdk.coders.Coder[${fieldType}]]""",
silent = false
)
val fieldCoderExpression =
q"private val ${fieldCoderName}: org.apache.beam.sdk.coders.Coder[${fieldType}] = ${fieldCoderInstance}"
val fieldEncodeExpression =
q"${fieldCoderName}.encode(value.${fieldTerm}, os)" // replace with full relative name (with dots) instead of value
val fieldDecodeExpression =
q"${field.asTerm} = ${fieldCoderName}.decode(is)"
(fieldCoderExpression, fieldEncodeExpression, fieldDecodeExpression)
}
val fieldCodersExpression = expressions.map(_._1).distinct
val coderEncodeExpresions = expressions.map(_._2)
val coderDecodeExpresions = expressions.map(_._3)
val coderExpression =
q"""{
new org.apache.beam.sdk.coders.Coder[${tpe}] {
{import ${c.prefix}._}
..${fieldCodersExpression}
override def encode(value: ${tpe}, os: java.io.OutputStream): _root_.scala.Unit = {
..${coderEncodeExpresions}
}
override def decode(is: java.io.InputStream): ${tpe} = {
${tpe.typeConstructor}(
..${coderDecodeExpresions}
)
}
override def getCoderArguments: java.util.List[_ <: org.apache.beam.sdk.coders.Coder[_]] = {
java.util.Collections.emptyList
}
override def verifyDeterministic(): _root_.scala.Unit = ()
}
}
"""
val ret = coderExpression
c.Expr[Coder[P]](ret)
}
But get an error after invoking sbt Test / compile:
(a bit struggling with imports and implicit search so for now having intermediate private vals, and distinct is useless)
{
final class $anon extends org.apache.beam.sdk.coders.Coder[com.github.fpopic.scalamacros.beam.Pojo] {
def <init>() = {
super.<init>();
()
};
{
import DefMacroCoder._;
()
};
private val coder$macro$1: org.apache.beam.sdk.coders.Coder[String] = scala.Predef.implicitly[org.apache.beam.sdk.coders.Coder[String]](DefMacroCoder.stringCoder);
private val coder$macro$2: org.apache.beam.sdk.coders.Coder[Int] = scala.Predef.implicitly[org.apache.beam.sdk.coders.Coder[Int]](DefMacroCoder.intCoder);
private val coder$macro$3: org.apache.beam.sdk.coders.Coder[List[Int]] = scala.Predef.implicitly[org.apache.beam.sdk.coders.Coder[List[Int]]](DefMacroCoder.listCoder[Int](DefMacroCoder.intCoder));
override def encode(value: com.github.fpopic.scalamacros.beam.Pojo, os: java.io.OutputStream): _root_.scala.Unit = {
coder$macro$1.encode(value.s, os);
coder$macro$2.encode(value.i, os);
coder$macro$3.encode(value.l, os)
};
override def decode(is: java.io.InputStream): com.github.fpopic.scalamacros.beam.Pojo = com.github.fpopic.scalamacros.beam.Pojo(s = coder$macro$1.decode(is), i = coder$macro$2.decode(is), l = coder$macro$3.decode(is));
override def getCoderArguments: java.util.List[_$1] forSome {
<synthetic> type _$1 <: org.apache.beam.sdk.coders.Coder[_$2] forSome {
<synthetic> type _$2
}
} = java.util.Collections.emptyList;
override def verifyDeterministic(): _root_.scala.Unit = ()
};
new $anon()
}
[error] .../DefMacroCoderSpec.scala:17:56: com.github.fpopic.scalamacros.beam.Pojo does not take parameters
[error] val coder: Coder[Pojo] = DefMacroCoder.productCoder[Pojo]
[error] ^
[error] one error found
Which I believe comes from here but don't fully understand what the compiler is trying to tell me?
Link to the full code sample can be found here
Link to CI error can be found here.
The way you're instantiating your class is wrong:
${tpe.typeConstructor}(...)
It should be
new $tpe(...)
or if you want to do it with case class companion object's apply instead of plain constructor:
${tpe.typeSymbol.companion}(...)
NOTE: type constructor (also known as higher kinded type) has nothing to do with class constructor
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.
Suppose I have some abstract value field defined in a trait:
trait Base {
val toBeOverride: String
}
case class Impl(other:Int) extends Base {
override val toBeOverride = "some value"
}
How can I write a function that I can easily get a cloned instance only overriding the toBeOverride value, like this:
// copy only available to case class instance
// v does not have method 'copy'
def overrideBaseValue[T <: Base](v: Base) =
v.copy(toBeOverride = "prefix" + v.toBeOverride)
?
Edit
#som-snytt, I don't think this is a duplicate, just like a Trait is not the same as an Abstract Class. And the answers of that question do not satisfy me, see below.
#Blaisorblade, yes, it is a problem. For instances of each sub case class, the toBeOverride field are the same, so it should not appear in the constructor.
For now all the suggestions are to define an customized copy method in each(!) sub case class and that in my opinion is ugly and shows the incapability of the language.
The simplest solution is to just add the method you want to Base:
trait Base {
val toBeOverride: String
def copyBase(newToBeOverridden: String): Base
}
case class Impl(other:Int, override val toBeOverride: String = "some value") extends Base {
def copyBase(newToBeOverridden: String) = copy(toBeOverride = newToBeOverridden)
}
This also allows to directly create an instance of Impl while specifying the value of toBeOverride (which wasn't possible). The only disadvantage is that now pattern matches using Impl have to change syntax - please update your question and add a comment if that's a problem.
BTW, if you just want to add a prefix (as in your example), that's no problem:
case class Impl(other:Int, override val toBeOverride: String = "some value") extends Base {
def copyBase(newToBeOverridden: String) = copy(toBeOverride = toBeOverride + newToBeOverridden)
}
Here are two mechanisms.
Apparently, in the near future you'll be able to write a macro that can emit the anonymous subclass, but until then, I think this typeclass is not arduous.
Just kicking the tires on Dynamic here.
import scala.language.dynamics
import scala.reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
trait Base {
def m: String
}
case class Impl(p: Int) extends Base {
override val m = "some value"
}
trait Basic extends Dynamic {
protected def m: String
def selectDynamic(f: String): Any =
if ("m" == f) m else reflecting(this, f)
protected def reflecting(b: Basic, f: String) = {
val im = cm.reflect(b)
val member = im.symbol.typeSignature member newTermName(f)
require(member != NoSymbol, s"No such member $f")
(im reflectMethod member.asMethod)()
}
}
case class Implic(p: Int) extends Basic {
override protected val m = "some value"
}
object Test extends App {
implicit class Copy[A <: Base](val b: A) {
def overriding(overm: String): A = (b match {
case impl: Impl => new Impl(impl.p) { override val m = overm }
case b: Base => new Base { override val m = overm }
}).asInstanceOf[A]
}
implicit class Proxy[A <: Basic : ClassTag](val b: A) {
def proximately(overm: String): Basic = new Basic {
override val m = overm
override def selectDynamic(f: String): Any =
if ("m" == f) overm else reflecting(b, f)
override def toString = b.toString
}
}
// asked for this
//def overriding[T <: Base](v: Base) = v.copy(m = "prefix" + v.m)
/* want something like this
def overriding[T <: Base](v: Base) = new Impl(v.p) {
override val m = "some value"
} */
val a = Impl(5)
val b = a overriding "bee good"
Console println s"$a with ${a.m} ~> $b with ${b.m}"
// or
val c = Implic(7)
val d = c proximately "dynomite"
Console println s"$c with ${c.m} ~> $d with ${d.m}"
}
Since traits don't get copy methods automatically, you can try using a Base case class instead:
case class Base(toBeOverride: String)
case class Impl(other: Int, someVal: String = "some value") extends Base(someVal)
def overrideBaseValue[T <: Base](v: Base) =
v.copy(toBeOverride = "prefix" + v.toBeOverride)
The problem that you're going to run into though, is that copy returns an instance of Base and I don't think that you can convert it back to your original Impl class. For instance, this won't compile:
def overrideBaseValue[T <: Base](v: T): T =
v.copy(toBeOverride = "prefix" + v.toBeOverride)
I am new to Scala and dont know why i have to do an (unintuitive for me) type cast related to path dependent types in the following code.
(I don't like getters, setters nor nulls, they are here to separate operations and disambiguate the source of errors)
// Module A public API
class ModA {
trait A
}
// Module B public API that depends on types defined in Module A
class ModB(val modA: ModA) {
trait B {
def getA: modA.A;
def setA(anA: modA.A);
}
}
// One implementation of Module A
class ModAImpl extends ModA {
class AImpl extends A
}
// One implementation of Module B
class ModBImpl(mod: ModA) extends ModB(mod) {
class BImpl extends B {
private[this] var privA: modA.A = _;
override def getA = privA;
override def setA(anA: modA.A) = privA = anA;
}
}
object Main {
def main(args: Array[String]): Unit = {
// wiring the modules
val modAImpl = new ModAImpl;
val modBImpl = new ModBImpl(modAImpl);
// wiring objects
val a = new modAImpl.AImpl;
val b = new modBImpl.BImpl;
b.setA(a); //don't compile and complain: type mismatch; found: modAImpl.A required: modBImpl.modA.A
//i have to do this horrible and coutnerintuitive cast to workaround it
b.setA(a.asInstanceOf[modBImpl.modA.A]);
var someA: modAImpl.A = null;
someA = b.getA; // don't compile with same reason
someA = b.getA.asInstanceOf[modAImpl.A]; // horrible cast to workaround
println(a == b.getA); // however this prints true
println(a eq b.getA); // this prints true too
}
}
I have read about singleton types to inform the compiler when two types are the same, but I don't know how to apply this here.
Thanks in advance.
You can stick a type parameter on the ModB types:
class ModA { trait A }
class ModB[AA](val modA: ModA { type A = AA }) {
trait B {
def getA: AA
def setA(anA: AA)
}
}
class ModAImpl extends ModA { class AImpl extends A }
class ModBImpl[AA](
mod: ModA { type A = AA }) extends ModB(mod) {
class BImpl extends B {
private[this] var privA: AA = _
override def getA = privA
override def setA(anA: AA) = privA = anA
}
}
And the type inference all works out as desired:
scala> val modAImpl = new ModAImpl
modAImpl: ModAImpl = ModAImpl#7139edf6
scala> val modBImpl = new ModBImpl(modAImpl)
modBImpl: ModBImpl[modAImpl.A] = ModBImpl#1dd7b098
scala> val a = new modAImpl.AImpl
a: modAImpl.AImpl = ModAImpl$AImpl#4cbde97a
scala> val b = new modBImpl.BImpl
b: modBImpl.BImpl = ModBImpl$BImpl#63dfafd6
scala> b.setA(a)
Let's start by simplifying your code ridding out the unnecessary complexity.
class Aout {
class Ain
}
class Bout(val link: Aout) {
class Bin(val field: link.Ain)
}
object Main {
def main(args: Array[String]): Unit = {
// wiring outer object
val aout: Aout = new Aout;
val bout: Bout = new Bout(aout);
// wiring inner object
val ain: aout.Ain = new aout.Ain;
val bin: bout.Bin = new bout.Bin(ain); //don't compile and complain: type mismatch; found: aout.Ain required: bout.link.Ain
}
}
Answer:
The compiler complains with a type mismatch error because he compares the paths of the two declared types involved in the assignment, and they are different.
The compiler is not intelligent enough to notice that at that point aout eq bout.link. You have to tell him.
So, replacing the line
val ain: aout.Ain = new aout.Ain;
with
val ain: bout.link.Ain = new bout.link.Ain;
solves the path-dependent type mismatch.
Anyway, correcting the type's path in your original code is not enough because there is also an inheritance problem.
One solution to that is to make the class ModBImpl know the ModAImpl class like this:
class ModA {
trait A
}
class ModB[M <: ModA](val modA: M) { // type parameter added
trait B {
def getA: modA.A;
def setA(anA: modA.A);
}
}
class ModAImpl extends ModA {
class AImpl extends A
}
class ModBImpl(mod: ModAImpl) extends ModB(mod) { // changed type of `mod` parameter from `ModA` to `ModAImpl`
class BImpl extends B {
private[this] var privA: modA.A = _;
override def getA: modA.A = privA;
override def setA(anA: modA.A): Unit = privA = anA;
}
}
object Main {
def main(args: Array[String]): Unit = {
val modAImpl = new ModAImpl;
val modBImpl = new ModBImpl(modAImpl);
val a: modBImpl.modA.AImpl = new modBImpl.modA.AImpl; // changed the path of the type
val b: modBImpl.BImpl = new modBImpl.BImpl;
b.setA(a); // here IntellijIde complains with a type mismatch error, but the ScalaIDE (eclipse) and the scala compiler work fine.
}
}
If the rules of your business don't allow that the ModBImpl class has knowledge of the ModAImpl class, tell me so we can find another solution.
If I have a class C defined as
class C[A]
is there any way to create a new instance of A within C? Something like
class C[A] {
def f(): A = new A()
}
I understand that, if this were possible, you'd probably have to specify the constructor arguments somewhere, and that's fine.
If it's not possible, are there any design patterns for dealing with the sort of situation where you'd like to create a new instance of a type?
You could use a type class to abstract instantiation:
trait Makeable[T] {
def make: T
}
class C[T: Makeable] {
def f(): T = implicitly[Makeable[T]].make
}
For example,
implicit object StringIsMakeable extends Makeable[String] {
def make: String = "a string"
}
val c = new C[String]
c.f // == "a string"
When you instantiate C, you'll need to provide, explicitly or implicitly, a Makeable that will act as a factory of the appropriate type. That factory, of course, would be responsible for supplying any constructor arguments when it invokes the constructor.
Alternatively, you could use a Manifest, but be warned that this approach relies on reflection and is not type safe:
class C[T: Manifest] {
def f(): T = manifest[T].erasure.newInstance.asInstanceOf[T]
}
For completeness, you can also easily extend this approach to pass some or all of the constructor parameters in to the make method:
trait Makeable[Args, T] { def make(a: Args): T }
class C[Args, T](implicit e: Makeable[Args, T]) {
def f(a: Args): T = e.make(a)
}
// some examples
case class Person(firstName: String, lastName: String)
implicit val personFactory1 = new Makeable[(String, String), Person] {
def make(a: (String, String)): Person = Person(a._1, a._2)
}
implicit val personFactory2 = new Makeable[String, Person] {
def make(a: String): Person = Person(a, "Smith")
}
val c1 = new C[String, Person]
c1.f("Joe") // returns Person("Joe", "Smith")
val c2 = new C[(String, String), Person]
c2.f("John", "Smith") // returns Person("John", "Smith")
You can demand an implicit parameter, like so:
class A[T](implicit newT : T) {
val t = newT
}
All you need then is to have an implicit factory of the desired type in scope when you instanciate A, e.g. the following works:
implicit def newSeq[T] = Seq[T]()
val a = new A[Seq[String]]
As shown by:
scala> a.t
res22: Seq[String] = List()
The same as #Raphael's answer with a case class's apply method:
class Container[A](contained: A)
case class Person(name: String)
case class PersonContainer(person: Person) extends Container[Person](person)
implicit def _ = PersonContainer.apply _
class Creator {
def deserializeAndPackage[A, B <: Container[A]](data: Array[Byte])
(implicit containerCreator: (A => B)): B = {
val p = /* deserialize data as type of A */
containerCreator(p)
}
}