I was trying to implement a similar structure with an idea to have a common trait to work with different Input instances regardless of their InType.
trait AnyInput {
type InType
val obj : InType
}
abstract class Input[T](obj: T) extends AnyInput {
type InType = T
}
case class InpImage(image: ByteStream) extends Input[ByteStream](image)
case class InpString(text: String) extends Input[String](text)
.
.
.
trait InputProcessor[T <: Input[T#InType]] {
...
}
and I get the "cyclic reference involving type T error" in the InputProcessor definition
It is important to notice, that there might be a couple different case class implementing Input[String] or Input[ByteStream]. So writing it out as
case class StringInput(s: String) extends Input[String](s)
case class IntInput(numb: Int) extends Input[Int](numb)
is not the best workaround
Maybe you can use
trait InputProcessor[S, T <: Input[S]] {
// ...
}
you can try something like this
trait InputProcessor[S <: AnyInput, T <: Input[S#InType]] {
implicit val evidence: T =:= S = implicitly[T =:= S]
}
and than the implementation would be:
class ImageProcessor extends InputProcessor[InpImage, InpImage] { }
you can read more about =:= here: https://stackoverflow.com/a/3427759/245024
EDIT:
added an example and the implementation to the evidence
notice that when extending InputProcessor, if you pass 2 different type parameters, the code won't compile (as you would expect)
Related
I was running some experiments around Aux-pattern and figured out that Aux is somewhat better integrated with compiler. Consider the following two cases:
I.
import Granularity.{Full, Partial}
sealed trait Granularity
object Granularity {
case object Full extends Granularity
sealed trait Partial extends Granularity {
type GranularityKey
}
object Partial{
type Aux[GK] = Partial{ type GranularityKey = GK }
}
}
sealed trait TestBub{
type G <: Granularity
}
object TestBub{
type Aux[GG <: Granularity] = TestBub{ type G = GG }
case class T1(i: Int) extends TestBub{
type G = Full.type
}
case class T2[Gr](j: String) extends TestBub{
type G = Partial.Aux[Gr]
}
def full[G <: Full.type](t: TestBub.Aux[G]): String = t match {
case T1(i) => i.toString
}
}
Scastie demo
This code compiles fine with no warning.
II.
import Granularity.{Full, Partial}
sealed trait Granularity
object Granularity {
case object Full extends Granularity
sealed trait Partial extends Granularity {
type GranularityKey
}
object Partial{
type Aux[GK] = Partial{ type GranularityKey = GK }
}
}
sealed trait TestBub[G <: Granularity]
object TestBub{
case class T1(i: Int) extends TestBub[Full.type]
case class T2[Gr](j: String) extends TestBub[Partial.Aux[Gr]]
def full[G <: Full.type](t: TestBub[G]): String = t match {
case T1(i) => i.toString
}
}
Scastie demo
This code compiles with a warning.
match may not be exhaustive.
It would fail on the following input: T2(_)
QUESTION: Taking these two cases into account does Aux pattern provide a better way to pattern match on a subset of all ADT branches?
This is yet another problem with Scala 2 typesystem. Scala 3 compiles the second case with no warnings (as expected).
I recently discovered that Scala compiler has an interesting feature for case class: Since it generates both a class & an object signature, if defined as an inner class, it can be used to override an abstract type definition and a function definition of its super class with minimal boilerplate code, here is an example:
object InnerCaseClassOverridingBoth {
trait AALike
trait SS {
type AA <: AALike
def AA(): AnyRef
}
trait SS_Clear extends SS {
def AA(): AnyRef
}
class SSA extends SS_Clear {
case class AA() extends AALike
}
object SSA extends SSA {}
}
This will compile without any error. However the shortcut stops here, if the function definition def AA is parameterized, then neither the inner case class nor inner object is capable of overriding it: the apply function of the inner object doesn't automatically expand to a method of its outer class:
trait SS_Parameterised extends SS {
def AA(ii: Int): AnyRef
}
class SSB extends SS_Parameterised {
case class AA(ii: Int) extends AALike
}
object SSB extends SSB {}
This gives an error:
class SSB needs to be abstract, since method AA in trait
SS_Parameterised of type (ii: Int)AnyRef is not defined
class SSB extends SS_Parameterised {
My question is, is there a shortcut in this case? Why is the Scala compiler is designed to link case 1 but not case 2?
It's not particularly designed at all; or, it is, but not in the way you seem to think. You aren't overriding def AA() with a method that constructs AA, you are overriding it with the object AA itself. Notice
trait T {
type I <: AnyRef
def I(): AnyRef
}
object O extends T {
case class I(val i: Int)
}
This works fine.
> (O: T).I()
I
> (O: T).I().getClass
class O$I$
> O.I(5)
I(5)
> O.I(5).getClass
class O$I
The salient design choices are "objects can override no-param defs" (and so can vals, vars and, of course, no-param defs) and "case classes automatically generate objects". "Inner case classes override methods of the same name in their outer class with their constructors," is not one of Scala's rules. object O contains a case class I and an object I, and the abstract def I(): AnyRef is overridden to return said object I. The contents of object I don't matter, because def I() only has to return an AnyRef, which means no restrictions are imposed. It makes perfect sense that
trait U {
type I <: AnyRef
def I(i: Int): AnyRef
}
object P extends U {
case class I(i: Int)
}
fails, then. object P contains a case class I and an associated object I, but it also needs a def I(i: Int): AnyRef, which it lacks.
I am guessing it is simply related to the role apply plays in case classes. See Case Class default apply method
SSA satisfies SS_Clear.AA via companion object of SSA (SSA.apply).
When you add a parameter to the method you no longer have the 0-parameter apply method to fulfill that role.
OK I found 2 ways of doing this
Method 1: overriden by case class:
trait SS_Parameterised {
type AA <: AALike
def AA: Int => AnyRef
}
Method 2: overriden by implicit class:
trait SS_Parameterised {
type AA <: AALike
implicit def AA(ii: Int): AnyRef
}
class SSB extends SS_Parameterised {
implicit class AA(ii: Int) extends AALike
}
End of story :) One case class overriding 2 declarations? No problem.
(Method 2 works as scala internally generates an implicit function for every implicit class)
I have the following relations:
trait Instrument
trait EquityOption extends Instrument { ... }
case class CallEquityOption(...) extends EquityOption
case class PutEquityOption(...) extends EquityOption
trait Priceable[I <: Instrument] { def price(I : Instrument) }
I can use exactly the same implementation of Priceable for the case classes CallEquityOptionand PutEquityOption. By having a match case to differentiation between the Call... and Put.... However, if I try to implement it directly as Priceable[EquityOption] under object EquityOption, the implicit cannot be found since it doesn't exactly match the type.
How can I make it work without needing to duplicate code?
You'll have to prove that you can provide an instance for every subtype of EquityOption.
implicit def allEquityOptions[T <: EquityOption]: Pricable[T] = ???
How can one let a superclass have access to its concrete instance's type?
class Base {
val t = typeOf[this.type]
}
class X extends Base {
}
assert((new X).t =:= typeOf[X]) <-- fails!!!
So, the idea is that Base.t should reflect the concrete type of the instance...
It's unfortunately a common misunderstanding of this.type: it isn't the class of the instance, it's the singleton type (i.e. the type which only has one member: this). It won't work without inheritance either.
This can be done using F-bounded polymorphism:
class Base[A <: Base[A] : TypeTag] {
val t = typeOf[A]
}
class X extends Base[X]
How about making t a method and making that generic.
import scala.reflect.runtime.universe._
class Base {
def myActualType[T <: Base](b: T)(implicit tt: TypeTag[T]) = typeOf[T]
}
class Foo extends Base
class Bar extends Foo
val bar = new Bar
assert(bar.myActualType(bar) =:= typeOf[Bar])
The downside is that you always have to send the object reference to it when you call it, but you get what you want.
I'm trying to implement a few structures from Okasaki's book in Scala, and in tests try to keep the actual tests in the base class, only using subclasses to provide the instance-under-test.
For example, a test for unbalanced (tree) set looks as follows:
class UnbalancedSetSpec
extends SetSpec(new UnbalancedSet[Int])
with IntElements
where
abstract class SetSpec[E, S](val set: Set[E, S]) extends Specification with ScalaCheck {
implicit def elements: Arbitrary[E]
// ...
private def setFrom(es: Seq[E]): S = es.foldRight(set.empty)(set.insert)
}
Now sometimes I want to specialise the child spec, e.g.
class RedBlackSetSpec
extends SetSpec(new RedBlackSet[Int])
with IntElements {
"fromOrdList" should {
"be balanced" ! prop { (a: List[Int]) =>
val s = RedBlackSet.fromOrdList(a.sorted)
set.isValid(s) should beTrue
}
}
}
it fails because there's no method isValid on Set[E, S] — it's defined in RedBlackSet[E]. But if I go ahead and change SetSpec[E, S](val set: Set[E, S]) to SetSpec[E, S, SES <: Set[E, S]](val set: SES), this particular problem disappears, but the code still fails to compile:
Error:(7, 11) inferred type arguments [Nothing,Nothing,okasaki.RedBlackSet[Int]] do not conform to class SetSpec's type parameter bounds [E,S,SES <: okasaki.Set[E,S]]
extends SetSpec(new RedBlackSet[Int])
^
Error:(7, 11) inferred type arguments [Nothing,Nothing,okasaki.UnbalancedSet[Int]] do not conform to class SetSpec's type parameter bounds [E,S,SES <: okasaki.Set[E,S]]
extends SetSpec(new UnbalancedSet[Int])
^
The definition of RedBlackSet is as follows:
package okasaki
class RedBlackSet[E](implicit ord: Ordering[E]) extends Set[E, RBTree[E]] {
so I would expect E to be inferred as Int rather than Nothing, and S as RBTree[Int] — but it doesn't happen.
class RedBlackSetSpec
extends SetSpec[Int, RedBlackSet.RBTree[Int], RedBlackSet[Int]](new RedBlackSet[Int])
with IntElements {
and
class UnbalancedSetSpec
extends SetSpec[Int, BinaryTree[Int], UnbalancedSet[Int]](new UnbalancedSet[Int])
with IntElements
work fine, but look ugly.
I'm struggling to understand why E and S are not inferred here. Any hints?
This is actually a well-known problem with Scala type inference: it can't infer SES "first" and use it to infer E and S. One solution comes to mind:
class RedBlackSetSpec(override val set: RedBlackSet[Int]) extends SetSpec(set) with IntElements {
def this() = this(new RedBlackSet[Int])
...
}
It becomes less ugly if you make set in SetSpec an abstract val instead of a constructor argument, but with a slight tradeoff in cases you don't need to specialize. I think there should be a better one, but this should work.