How do generic types work with inheritance in scala? - scala

I'm trying to understand how generics work with inheritance in Scala.
I have the following code:
sealed trait Model {}
case class Model1() extends Model
case class Model2() extends Model
trait Repo[A <: Model] {
def doSomething(model: A)
}
class Repo1 extends Repo[Model1] {
override def doSomething(model: Model1): Unit = {
println("Model 1")
}
}
class Repo2 extends Repo[Model2] {
override def doSomething(model: Model2): Unit = {
println("Model 2")
}
}
object Play extends App {
def getModel(i: Int): Model =
i match {
case 1 => Model1()
case 2 => Model2()
case _ => throw new RuntimeException("model not found")
}
val model = getModel(1)
val repo = model match {
case _: Model1 => new Repo1
case _: Model2 => new Repo2
case _ => throw new RuntimeException("something went wrong")
}
repo.doSomething(model)
}
On the last line repo.doSomething(model) I get Type mismatch. Required: _.$1 Found: Model
According to this answer What is the correct way to implement trait with generics in Scala? if my repos classes extend the trait with the type should work.
I'm new to Scala and I'm trying to wrap my head around the type system, generics, implicit, upper/lower bounds ...
What is _.$1 type and how can I make this work? Thanks!

scala is statically typed, and the value model is of compile time type Model and repo of the compile time type Repo
So repo.doSomething is no further refined. The signature of doSomething says it'll take some subtype of Model of a parameter, but we don't know which one -- in other words, the compiler doesn't know that the type of model and the type of repo align.
To make them align, you have a few options.
Because you know that the types align because you constructed it in a way where you know more than the compiler, tell the compiler so
val castRepo = repo.asInstanceOf[Repo[Any]]
That turns off the safeties, and you tell scala "trust me, I know what I'm doing". This is fine to some extent when you know what you're doing, but people who really know what they're doing tend to not trust themselves to know better than the compiler, so a different solution that does retain type safety is probably better.
restructure the program so that things do align.
You could make a wrapper type for example, like so
case class Aligned[A <: Model](model: A, repo: Repo[A]) {
def doIt = repo.doSomething(model)
}
val aligned = model match {
case m: Model1 => Aligned(m, new Repo1)
case m: Model2 => Aligned(m, new Repo2)
case _ => throw new RuntimeException("something went wrong")
}
aligned.doIt
Within Aligned, scalac knows the Model type and the Repo type line up.
You don't even really need the instance method doIt; aligned.repo.doSomething(aligned.model) also works, because the compiler knows the A in aligned.repo and the A in aligned.model are both the same A in aligned.

Related

Type-safety for Patternmatching on Parameters with Dependent Types in Scala

I am currently working with a type hierarchy which represents a tree and an accompanying type hierarchy that represents steps of root-to-node paths in that tree. There are different kinds of nodes so different steps can be taken at each node. Therefore, the node types have a type member which is set to a trait including all valid steps. Take the following example:
// Steps
sealed trait Step
sealed trait LeafStep extends Step
sealed trait HorizontalStep extends Step
sealed trait VerticalStep extends Step
object Left extends HorizontalStep
object Right extends HorizontalStep
object Up extends VerticalStep
object Down extends VerticalStep
// The Tree
sealed trait Tree {
type PathType <: Step
}
case class Leaf() extends Tree {
override type PathType = LeafStep
}
case class Horizontal(left: Tree, right: Tree) extends Tree {
override type PathType = HorizontalStep
}
case class Vertical(up: Tree, down: Tree) extends Tree {
override type PathType = VerticalStep
}
In this example, given a tree the path Seq(Up, Right) would tell us to go to the "right" child of the "up" child of the root (assuming the tree's nodes have the fitting types). Of course, navigating the tree involves a bunch of code using PartialFunctions that is not shown in the example. However, in that process I want to provide a type-safe callback which is notified for every step that is taken and its arguments include both the step and the respective tree nodes.
My current approach is a function def callback(from: Tree, to: Tree)(step: from.PathType). That works without problems on the caller side, but I'm running into an issue when actually implementing such a callback that works with the data passed to it.
def callback(from: Tree, to: Tree)(step: from.PathType) = {
from match {
case f: Horizontal => step match {
case Left => ??? // do useful stuff here
case Right => ??? // do other useful stuff here
}
case _ => ()
}
}
In the function, the compiler is not convinced that Left and Right are of type from.PathType. Of course, I could simply add .asInstanceOf[f.PathType] and the code seems to work with that. However, the outer match gives us that f is of type Horizontal. We know that PathType of Horizontal objects is HorizontalStep and since f is identical to from we also know that from.PathType is of that same type. Finally, we can check that both Left and Right extend HorizontalStep. So, the above code should always be type-safe even without the cast.
Is that reasoning something the Scala compiler just does not check or am I missing a case where the types could differ? Is there a better way to achieve my goal of a type-safe callback? I'm using Scala 2.12.15
I'm afraid I do not have a thorough explanation on why this does not typecheck. (It seems as if for such dependent-typed parameters, the compiler just isn't able to apply the knowledge that was gained from pattern matching onto the other parameter (that was not explictly matched)).
Nevertheless, here is something that would work:
First, use a type parameter instead of a type member:
// (Steps as above)
// The Tree
sealed trait Tree[PathType <: Step]
// type alias to keep parameter lists simpler
// where we do not care..
type AnyTree = Tree[_ <: Step]
case class Leaf() extends Tree[LeafStep]
case class Horizontal(left: AnyTree, right: AnyTree) extends Tree[HorizontalStep]
case class Vertical(up: AnyTree, down: AnyTree) extends Tree[VerticalStep]
Then, this would fully typecheck:
def callback[S <: Step, F <: Tree[S]](from: F, to: AnyTree)(step: S) = {
def horizontalCallback(from: Horizontal)(step: HorizontalStep) = {
step match {
case Left => ??? // do useful stuff here
case Right => ??? // do other useful stuff here
}
}
from match {
case f: Horizontal =>
horizontalCallback(f)(step)
case _ =>
()
}
}
Confusingly, the compiler would not be able to properly check the pattern match on the step if one places it directly in the outer match like this (Edit: that is only true for 2.12 where this does not give a "match may not be exhaustive" warning - with 2.13 or 3.2, this checks fine):
def callback[S <: Step, F <: Tree[S]](from: F, to: AnyTree)(step: S) = {
from match {
case f: Horizontal =>
step match {
case Left => ??? // do useful stuff here
case Right => ??? // do other useful stuff here
}
case _ =>
()
}
}

TypeTag for case classes

I would like to make a case class Bla that takes a type parameter A and it knows the type of A at runtime (it stores it in its info field).
My attempt is shown in the example below. The problem is that this example does not compile.
case class Bla[A] (){
val info=Run.paramInfo(this) // this does not compile
}
import scala.reflect.runtime.universe._
object Run extends App{
val x=Bla[Int]
def paramInfo[T](x:T)(implicit tag: TypeTag[T]): String = {
val targs = tag.tpe match { case TypeRef(_, _, args) => args }
val tinfo=s"type of $x has type arguments $targs"
println(tinfo)
tinfo
}
paramInfo(x)
}
However when I comment val info=Run.paramInfo(this) then the program runs fine and prints:
type of Bla() has type arguments List(Int)
Is there a way to make this example below compile ? (or in some other way achieve the same goal, i.e. that a case class is self aware of the type of it's type parameter?)
There's little point in using reflection based APIs for this, shapeless has a typeclass that exposes compile time information to runtime using an implicit macro.
import shapeless.Typeable
class Test[T : Typeable] {
def info: String = implicitly[Typeable[T]].describe
}
It's also relatively easy to roll your own thing here, with the added inconvenience of having to compile the implicit macro in a different compilation unit than whatever is using it.
You just need to pass the implicit type tag parameter to the case class constructor (otherwise the type information is lost before calling paraInfo which requires it):
case class Bla[A : TypeTag]() { ... }
Which is shorthand for:
case class Bla[A](implicit tag: TypeTag[A]) { ... }

Using 'nested' types in a list in Scala

I'm using ScalaFX and JavaFX, and have this code:
import scalafx.Includes._
class Type1(anInt: Int, ...)
class Type2(aString: String, ...)
class ListItem[T](internalValue:T, ...)
object Worker
{
val list1 = FXCollections.observableArrayList[ListItem[Type1]]()
val list2 = FXCollections.observableArrayList[ListItem[Type2]]()
def workWithList(list:ObservableList[ListItemType]) {
list.foreach(i => workWithItem(i))
}
def workWithItem(item:ListItem) {
item match {
case i:ListItem[Type1] => do something
case i:ListItem[Type2] => do something else
}
}
workWithList(list1)
workWithList(list2)
}
My problem is that this code doesn't compile; it says that I can't use ObservableList[ListItem[Type1]] for the workWithList method, which expects ObservableList[ListItem].
As I've been playing with this, some variations of this code says that there are unchecked warnings, and that pattern matching won't work due to type erasure.
Ideally:
there would be just a single list that could hold objects of type ListItem[Type1] and ListItem[Type2]
I could do pattern matching when working with the items to do different things depending on what kind of item is being worked with
workWithItem could work with either type of item. In my current code I've had to change the signature to workWithItem(item:ListItem[_]) and then do workWithItem(someItem.asInstanceOf[ListItem[_]]). Probably not the correct thing to do!
Thanks!
The method signature for workWithList looks wrong - where does the ListItemType type come from? Should this be def workWithList(list: ObservableList[ListItem[_]]) { ...?
If so, then the problem you will run up against in the match cases is that due to type erasure, the JVM can't tell the difference at runtime between the type signatures of the cases. This can be worked around by, for example, turning the Type1, Type2 and ListItem into case classes (or manually generating unapply methods for them), then deconstructing the item in the match cases, like so:
case class Type1(anInt: Int)
case class Type2(aString: String)
case class ListItem[T](internalValue:T)
object Worker
{
val list1 = FXCollections.observableArrayList[ListItem[Type1]]()
val list2 = FXCollections.observableArrayList[ListItem[Type2]]()
def workWithList(list: ObservableList[ListItem[_]]) {
list.foreach(i => workWithItem(i))
}
def workWithItem(item: ListItem[_]) {
item match {
case ListItem(i: Type1) => println(s"Have type 1: ${i.anInt}") //do something
case ListItem(i: Type2) => println(s"Have type 2: ${i.aString}") //do something else
case anythingElse => println(s"Unknown type: $anythingElse") //just as a safe default for now
}
}
workWithList(list1)
workWithList(list2)
}
Note that I am working here without specific knowledge of the FX libraries (I tried this using straight scala Lists rather than ObservableList or FXCollections.observableArrayList), so they may affect the applicability of this solution (and might be where ListItemType is defined).
The method signature for workWithItem is fine, but the asInstanceOf cast you tried shouldn't be required.
You are attacking mosquito with a shotgun. This example can be solved without parametric polymorphism, with plain old inheritance:
import scalafx.Includes._
import javafx.collections.{FXCollections,ObservableList}
class ListItemType
class Type1(anInt: Int) extends ListItemType
class Type2(aString: String) extends ListItemType
class ListItem(val internalValue:ListItemType)
object Worker
{
val list1 = FXCollections.observableArrayList[ListItem]()
val list2 = FXCollections.observableArrayList[ListItem]()
def workWithList(list:ObservableList[ListItem]) {
list.foreach(i => workWithItem(i))
}
def workWithItem(item:ListItem) {
item.internalValue match {
case i:Type1 => println("do something")
case i:Type2 => println("do something else")
}
}
workWithList(list1)
workWithList(list2)
}
No errors, no warnings, and you can mix both types of objects in the same list.

Dependent method types and type-classes

I've got a bunch of data store type-classes that look all the same.
trait FooStore[C] {
def create(f: FooId => Foo)(c: C): Foo
// update and find methods
}
I'd like to simplify things and was hoping to use dependent method types to get something closer to
sealed trait AR {
type Id
type Type
}
sealed trait FooAR extends AR {
type Id = FooId
type Type = Foo
}
trait DataStore[C] {
def create(ar: AR)(f: ar.Id => ar.Type)(c: C): ar.Type
}
but when I try and create an instance of that as follows
case class InMemory(foos: List[Foo])
object InMemory {
lazy val InMemoryDataStore: DataStore[InMemory] = new DataStore[InMemory] {
def create(ar: AR)(f: ar.Id => ar.Type)(c: InMemory): ar.Type = sys.error("not implemented")
}
}
I get the following compile error
object creation impossible, since method create in trait DataStore of type (ar: AR)(f: ar.Id => ar.Type)(c: InMemory)ar.Type is not defined
lazy val InMemoryDataStore: DataStore[InMemory] = new DataStore[InMemory] {
^
one error found
I don't understand since that method is pretty clearly defined on the DataStore instance. What does the error mean and is this possible? If not, is there a different way to accomplish the same thing?
It compiles using the Scala-2.10-M2 milestone, some dependent method type bugs have been fixed since the 2.9 release. I'm not completely sure, but perhaps this one might have made it work.

Best way to use type classes with list parametrized with some base class, abstract class or trait

I think it would be easier to describe a problem with concrete example. Suppose I have have Fruit class hierarchy and Show type class:
trait Fruit
case class Apple extends Fruit
case class Orange extends Fruit
trait Show[T] {
def show(target: T): String
}
object Show {
implicit object AppleShow extends Show[Apple] {
def show(apple: Apple) = "Standard apple"
}
implicit object OrangeShow extends Show[Orange] {
def show(orange: Orange) = "Standard orange"
}
}
def getAsString[T](target: T)(implicit s: Show[T]) = s show target
I also have list of fruits that I would like to show to the user using Show (this is my main goal in this question):
val basket = List[Fruit](Apple(), Orange())
def printList[T](list: List[T])(implicit s: Show[T]) =
list foreach (f => println(s show f))
printList(basket)
This will not compile because List is parametrized with Fruit and I have not defined any Show[Fruit]. What is the best way to achieve my goal using type classes?
I tried to find solution for this problem, but unfortunately have not found any nice one yet. It's not enough to know s in printList function - somehow it needs to know Show[T] for each element of the list. This means, that in order to be able to make this, we need some run-time mechanism in addition to the compile-time one. This gave me an idea of some kind of run-time dictionary, that knows, how to find correspondent Show[T] at run-time.
Implementation of implicit Show[Fruit]can serve as such dictionary:
implicit object FruitShow extends Show[Fruit] {
def show(f: Fruit) = f match {
case a: Apple => getAsString(a)
case o: Orange => getAsString(o)
}
}
And actually very similar approach can be found in haskell. As an example, we can look at Eq implementation for Maybe:
instance (Eq m) => Eq (Maybe m) where
Just x == Just y = x == y
Nothing == Nothing = True
_ == _ = False
The big problem with this solution, is that if I will add new subclass of Fruit like this:
case class Banana extends Fruit
object Banana {
implicit object BananaShow extends Show[Banana] {
def show(banana: Banana) = "New banana"
}
}
and will try to print my basket:
val basket = List[Fruit](Apple(), Orange(), Banana())
printList(basket)
then scala.MatchError would be thrown because my dictionary does not know anything about bananas yet. Of course, I can provide updated dictionary in some context that knows about bananas:
implicit object NewFruitShow extends Show[Fruit] {
def show(f: Fruit) = f match {
case b: Banana => getAsString(b)
case otherFruit => Show.FruitShow.show(otherFruit)
}
}
But this solution is far from perfect. Just imagine that some other library provides another fruit with it's own version of dictionary. It will just conflict with NewFruitShow if I try to use them together.
Maybe I'm missing something obvious?
Update
As #Eric noticed, there is one more solution described here: forall in Scala . It's really looks very interesting. But I see one problem with this solution.
If I use ShowBox, then it will remember concrete type class during it's creation time. So I generally building list with objects and correspondent type classes (so dictionary in present in the list). From the other hand, scala has very nice feature: I can drop new implicits in the current scope and they will override defaults. So I can define alternative string representation for the classes like:
object CompactShow {
implicit object AppleCompactShow extends Show[Apple] {
def show(apple: Apple) = "SA"
}
implicit object OrangeCompactShow extends Show[Orange] {
def show(orange: Orange) = "SO"
}
}
and then just import it in current scope with import CompactShow._. In this case AppleCompactShow and OrangeCompactShow object would be implicitly used instead of defaults defined in the companion object of Show. And as you can guess, list creation and printing happens in different places. If I will use ShowBox, than most probably I will capture default instances of type class. I would like to capture them at the last possible moment - the moment when I call printList, because I even don't know, whether my List[Fruit] will ever be shown or how it would be shown, in the code that creates it.
The most obvious answer is to use a sealed trait Fruit and a Show[Fruit]. That way your pattern matches will complain at compile time when the match is not exhaustive. Of course, adding a new kind of Fruit in an external library will not be possible, but this is inherent in the nature of things. This is the "expression problem".
You could also stick the Show instance on the Fruit trait:
trait Fruit { self =>
def show: Show[self.type]
}
case class Apple() extends Fruit { self =>
def show: Show[self.type] = showA
}
Or, you know, stop subtyping and use type classes instead.