Please have a look at the followin code, where Extractor[A,B] is part of a generic framework and everything else should be regarded as "client code" (I boiled it a down quite a bit and renamed everything. So don't mind that Extractor doesn't seem to be too usefull).
scala> abstract class Extractor[A,B] {
| def extract(d:A):B
| def stringRepr(d:A):String
| }
defined class Extractor
scala> sealed abstract class Value
defined class Value
scala> case class IntValue(i:Int) extends Value
defined class IntValue
scala> case class StringValue(s:String) extends Value
defined class StringValue
scala> case class Data(i:Int, s:String)
defined class Data
scala> sealed abstract class MyExtractor[Value] extends Extractor[Data, Value] {
| def stringRepr(d:Data) = extract(d) match {
| case IntValue(i) => i.toString
| case StringValue(s) => s
| }
| }
defined class MyExtractor
scala> class IntExtractor(name:String) extends MyExtractor[IntValue] {
| def extract(d:Data) = IntValue(d.i)
| }
defined class IntExtractor
scala> class StringExtractor(name:String) extends MyExtractor[StringValue] {
| def extract(d:Data) = StringValue(d.s)
| }
defined class StringExtractor
so in short words Extractor[A,B] is used to extract some value B from A and do some other things that are not represented in this show code. The abstract classes Value and MyExtractor are used for reasons of type savety in the "client code".
When I try to create a List of MyExtractors, the following happens:
scala> val l = List.empty[MyExtractor[Value]]
l: List[MyExtractor[Value]] = List()
scala> new IntExtractor("test1") :: l
res5: List[MyExtractor[_ >: IntValue <: Value]] = List(IntExtractor#1fd96c5)
trying to convert an IntExractor to a superclass
scala> new IntExtractor("test"):MyExtractor[Value]
<console>:24: error: type mismatch;
found : IntExtractor
required: MyExtractor[Value]
new IntExtractor("test"):MyExtractor[Value]
^
scala> new IntExtractor("test"):Extractor[Data,Value]
<console>:24: error: type mismatch;
found : IntExtractor
required: Extractor[Data,Value]
new IntExtractor("test"):Extractor[Data,Value]
I am aware that everything is fine, when I define IntExtractor like this
scala> class IntExtractor(name:String) extends MyExtractor[Value] {
| def extract(d:Data) = IntValue(d.i)
| }
defined class IntExtractor
scala> new IntExtractor("test"):Extractor[Data,Value]
res17: Extractor[Data,Value] = IntExtractor#1653d7a
But I don't understand, why it doesn't work the way I tried it above.
I would be thankfull for any help or hints.
As near as I can tell, the concept you are looking for is "covariance". Just because IntValue is a subtype of Value doesn't mean that MyExtractor[IntValue] is a subtype of MyExtractor[Value]. By default, there is no subtyping relation between those two types at all. To create such a relationship, you need to declare MyExtractor to be covariant with respect to it's parameter. Scala lets you declare type parameters to be covariant by adding a "+" before the type parameters declaration. This is called a variance notation.
sealed abstract class MyExtractor[+Value] extends Extractor[Data, Value] {
}
Scala also supports contravariance over type parameters. Contravariance is just like covariance, but reversed, and is expressed with a "-" variance notation on the type parameter. Your Extractor type provides an excellent example of a place where a contravariance notation makes sense.
abstract class Extractor[-A,+B] {
def extract(d:A):B
def stringRepr(d:A):String
}
This means that if Foo is a subtype of Bar, then Extractor[Bar, Baz] is a subtype of Extractor[Foo, Baz], which if you think about it makes sense. If something can extract the data you want when passed an instance of a supertype, then by definition it can extract it when passed an instance of a subtype. Conversely, if Foo is a subtype of Bar, then Extractor[Baz, Foo] is a subtype of Extractor[Baz, Bar]. That also makes sense. If you've got an extractor that returns a Foo, you can certainly use it wherever you need an extractor that returns a Bar.
There are restrictions on when contravariance and covariance can be declared. For instance, contravariant type parameters can only be used as method arguments, and covariant parameters can only be used as method returns or vals. Neither can be used as vars. It gets more complicated with nested type parameters, but the rules basically boil down to "where it's sensible", and your example meets all of them.
Additional side note, all of your abstract classes in your example should probably be declared as traits instead. As long as your abstract classes don't require constructor arguments, declaring them as traits gives you a few more opportunities for reuse.
Related
I have a question on when to use type parameterization vs base class type when defining a method/class in Scala especially when the types allowed are in same hierarchy - constrained using type bound.
for eg:
trait TypeA
case class SubTypeA1(in: String) extends TypeA
case class SubTypeA2(in: String) extends TypeA
case class NewType[T <: TypeA](inTypeA: T)
case class NewTypeV2(inTypeA: TypeA)
def testMethod1(in: String): NewType[TypeA] = NewType(SubTypeA1(in))
def testMethod2(in: NewType[TypeA]): Unit = print("Some functionality")
def testMethod3(in: String): NewTypeV2 = NewTypeV2(SubTypeA1(in))
def testMethod4(in: NewTypeV2): Unit = print("Some functionality")
In the above case/in general when constraining allowed types to a certain upper bound, what advantage would I get with NewType over NewTypeV2 or vice versa? They both look equivalent to me.
As I understand it, if I were to add some implicit condition check like NewType[T: Addable] thats present across types of different hierarchies then type parameterization would make sense, besides that what are the reasons I should prefer type parameterization over using Interface type or Base class type like the type of inTypeA member of NewTypeV2 case class.
Is defining type parameterized way like NewType[T] considered more "Functional" style over the other?
And Secondly, question on Variance vs Type bound. In the above code block NewType is Invariant on Type T, so NewType[SubTypeA1] is not a subtype of NewType[TypeA], they are unrelated, correct?
If my understanding on type invariance is correct as mentioned above, how is the testMethod1 compiling? As I am explicitly passing SubTypeA1 but still it gets casted to NewType[TypeA] and also it can be passed to testMethod2 without issue. What am I misunderstanding here?
scala> testMethod1("test")
res0: NewType[TypeA] = NewType(SubTypeA1(test))
scala> testMethod2(testMethod1("test"))
Some functionality
scala> NewType(SubTypeA1("tst"))
res3: NewType[SubTypeA1] = NewType(SubTypeA1(tst))
case class NewType[T <: TypeA](inTypeA: T)
case class NewTypeV2(inTypeA: TypeA)
The difference between these two is the type of inTypeA:
In NewType the type of inTypeA is the type parameter T which can be any subtype of TypeA. The compiler knows what the actual subtype is.
In NewTypeV2 the type of inTypeA is TypeA. The value will be a subtype of TypeA, but all the compiler knows is that it is a TypeA
This matters if you have a method that requires a particular subtype of TypeA:
def subtypeMethod(in: SubTypeA1) = ???
val a1 = SubTypeA1("string")
val n = NewType(a1)
subtypeMethod(n.inTypeA) // Yes, inTypeA is type SubTypeA1
val n2 = NewTypeV2(a1)
subtypeMethod(n2.inTypeA) //No, inTypeA is type TypeA
Abstract member method is illegal in a singleton object
scala> object Foo {
| def g: Int
| }
def g: Int
^
On line 2: error: only traits and abstract classes can have declared but undefined members
as is abstract value member
scala> object Foo {
| val x: Int
| }
val x: Int
^
On line 2: error: only traits and abstract classes can have declared but undefined members
however abstract type member is legal in a singleton object
scala> object Foo {
| type A
| }
object Foo
so clearly the sense in which a type member is abstract is different from other abstract members. What is the difference? How can an abstract type member be useful when it seems it cannot be made concrete since object is final?
Well, the things is type doesn't have to be concrete. You can write:
type Arbitrary
in Ammonite and it compiles and runs. You can even use it as argument!
type Arbitrary
def foo(a: Arbitrary): List[Arbitrary] = List(a)
The only issue is, that compiler doesn't know anything about Arbitrary (e.g. that it <: String or something) which would allow you to legally create a value of this type.
From that point of view, abstract type member is just a type we don't know anything about, but which we can around knowing only that it exist and value would be of this type.
But, we can also override this empty definition by making it more specific e.g.
type Arbitrary = String
type Arbitrary <: AnyVal
type Arbitrary >: User
Then whoever implements it will have access to full type information, while programmer writing code that has abstract definition in scope can only pass type around.
Accidentally, that's how we rediscovered path-dependent types, or rather figured out that being able to create abstract types is kind of necessary if we want to have path-dependent types without manually maintained list of cases where we don't want them.
In Cats in Scala 2 this has another use case. There is a pattern which apparently was discovered by Edward Kmett that uses abstract type member and .asInstanceOf to workaround missing polymorphic functions that we could lift to a type class:
trait DoubleSize[F[_]] {
def double[A](fa: F[A]): F[A]
}
object DoubleSize {
type Arbitrary
def instance[F[_]](fun: F[Arbitrary] => F[Arbitrary]): DoubleSize[F] = new DoubleSize[F] {
def double[A](fa: F[A]): F[A] = fun(fa.asInstanceOf[F[Arbitrary]]).asInstanceOf[F[A]]
}
// in Dotty we could do
// def instance[F[_]](fun: [A] => F[A] => F[A]) = new DoubleSize[F] {
// def double[A](fa: F[A]): F[A] = fun[A](fa)
// }
// but in Scala 2 it's impossible
}
val doubleSize = DoubleSize.instance[List] { list =>
list ++ list
}
doubleSize.double(List(1,2,3))
doubleSize.double(List("a", "b", "c"))
This kind of workarounds wouldn't be possible if we ruled out abstract type members, though in Dotty this particular workaround won't be necessary anymore.
Based on #tpolecat and Dmytro it seems abstract type member is related to an existential type
scala> object O {
| type A
| implicitly[A <:< (x forSome { type x })]
| }
object O
I'm a bit confused about understanding of what type in scala means.
In documents I read that List[Int] is a type and List is a type constructor.
but what keyword type means when I write the following?
val ls: scala.collection.immutable.List.type = scala.collection.immutable.List
And how this type related to type that can be defined as a field in a trait for example.
In a scala val assignment like the above:
val name: Tpe = expression
Tpe is the type of the identifier name. Hence:
val x: List.type = List
The type of the List object (aka List module or List companion) is List.type. This indicates that it is a singleton. All of this is completely orthogonal to the concept of a type constructor in type theory.
Type Constructors
In type theory, List (note: not the companion) is both a type (denoted *) and a type constructor (denoted * -> *) because when you supply a type argument to List (e.g. Int) then you have a type (i.e. List[Int]). Scala provides syntax for this construction. For example:
def foo[F[_]]: F[Int]
^^^^ ^^^
| + supply a type argument to F[_]
|
+ the type constructor F[_]
But you cannot really implement such a method unless you know more about F. For example, if there is a implicit scalaz.Monad[F], you might use the Monad.pure value to construct your F[Int] like so:
def foo[F[_]: Monad]: F[Int] = Monad[F].pure(1)
In Scala
Scala does let you pass around List as a type constructor as a type parameter to such a method. For example:
scala> def foo[F[_]]: F[Int] = ???
foo: [F[_]]=> F[Int]
scala> lazy val x = foo[List]
x: List[Int] = <lazy>
However the List you are supplying in foo[List] is not the companion
Note the declaration of foo will result in the following warning:
<console>:11: warning: higher-kinded type should be enabled
by making the implicit value scala.language.higherKinds visible.
This can be achieved by adding the import clause 'import
scala.language.higherKinds'
or by setting the compiler option -language:higherKinds.
See the Scaladoc for value scala.language.higherKinds for a discussion
why the feature should be explicitly enabled.
def foo[F[_]]: F[Int] = ???
^
what keyword type means when I write the following
type is a member defined on object in Scala.
Suppose you have a class and its companion object:
class Foo(a: String)
object Foo {
def apply(a: String) = new Foo(a)
}
Now suppose you want to write a method that accepts the object Foo as an input. What's its type?
If you write this:
def someMethod(fooObj: Foo) = fooObj.apply("x") // doesn't compile
it's not going to compile. Foo refers to the type of an instance of the class (i.e. the one returned by new Foo("x") or Foo("x")). That's why objects have a type member that can refer to their own type:
def someMethod(fooObj: Foo.type) = fooObj.apply("x") // compiles!
In your specific example List.type is the type of the companion object of List. Here's a couple of examples that I hope will clarify what it means:
val listObj: List.type = List
val anEmptyList: List[Int] = listObj.empty[Int] // List()
val aListOfIntegers: List[Int] = listObj.range(1, 4) // List[(1, 2, 3)
And how this type related to type that can be defined as a field in a trait for example.
The type keyword defines a type member. .type is a type member. Conceptually it's like every object in Scala has a type member named type, like this:
object Foo {
type type = Foo
}
Obviously this won't compile, but it gives you an idea of what it may look like.
In type-level programming you’d think of a List[+A] being a type constructor:
That is:
-List[+A] takes a type parameter (A),
-by itself it’s not a valid type, you need to fill in the A somehow - "construct the type",
-by filling it in with String you’d get List[String] which is a concrete type.
In Scala it is not valid to say something is of type List. Scala is more strict here, and won’t allow us to use just a List in the place of a type, as it’s expecting a real type - not a type constructor.
In brief:
List[+A] is a type constructor whereas List[String] is a real type.
I'm trying to test equality between a vector of generic types and a list of input parameters to a case class constructor. I've figured out how to use reflection to get the list of parameter and their types, but I can't figure out how to test for equality between the object instances and those types, when the object instance is a generic.
Here is a simplified example:
abstract class Foo[T]
case class Bar[T](value: Seq[T]) extends Foo[Seq[T]]
def getTypeTag[T: TypeTag](obj: T) = typeTag[T]
// In my use case I have a function that can return any
// Foo, which is why I've used the wildcarded generic here
val bar: Foo[_] = Bar[Int](Seq(2))
// In my use case I actually get the type for a parameter list
// of a case class constructor, but putting this here for simplicity
val bar2 = Bar(Seq(1))
var barType = getType(bar2)
I want to be able to test that bar is of type barType, but this is as close as I can get. I'm not sure how to get the type signature with the specified generic value.
scala>runtimeMirror(barClass.getClassLoader).classSymbol(barClass).toType
res112: reflect.runtime.universe.Type = Bar[T]
scala> getType(bar2)
res113: reflect.runtime.universe.Type = Bar[Int]
I'm pretty new to Scala (and JVM reflection in general), so thanks for the help .
You can create traits as parent for all the types you wish to use and then using pattern matching for type to get the correct object type at runtime. Scala has a very powerful pattern matching functionality.
This link might be useful: http://www.cakesolutions.net/teamblogs/ways-to-pattern-match-generic-types-in-scala
scala> case class Data[+T](val value:T=null)
defined class Data
scala> val foo=Data[ArrayBuffer[Data[Any]]]()
foo: Data[scala.collection.mutable.ArrayBuffer[Data[Any]]] = Data(null)
scala> foo.value+=Data[String]()
java.lang.NullPointerException
... 33 elided
I would like to have a Data class that is instantiated either as Data[String], Data[ArrayBuffer[Data[Any]]] or Data[Map[String,Data[Any]]]. In the above example I try to instantiate it as Data[ArrayBuffer[Data[Any]]] and add a Data[String] to its arraybuffer. Of course I get a null pointer exception because value is null. But the point of this example that it at least compiles and runs.
Now, in the Data constructor I would like to instantiate value as either a Data[String], an ArrayBuffer[Data[Any]] or Map[String,Data[Any]] depending on the type of the initially null value returned by the getClass method. However for this I need value to be a var, so that I can modify it after examining the type of its null value.
However I get this error:
scala> case class Data[+T](var value:T=null)
<console>:11: error: covariant type T occurs in contravariant position in type T of value value_=
case class Data[+T](var value:T=null)
Make your Data invariant in T. Just remove the +: Data[T] - this should compile.
Better yet, rethink your design to get rid of nulls and mutable variables - they both smell.
Edit: after reading your comments, I understand better what you are trying to do. Consider something like this for example as one of the options.
sealed trait Node
case class ListNode(list: Seq[Node]) extends Node
case class MapNode(map: Map[String, Node]) extends Node
case class LeafNode(data: String) extends Node
Now you can parse your document with something like (this is "pseudocode", adjust it to whatever xml-parsing library you are using):
def parseNode(tag: XMLTag): Node = tag.tagType match {
case LIST =>
val subNodes = tag.getNestedTags.map(parseNode)
ListNode(subNodes)
case MAP =>
val subNodes = tag.getNestedTags.map { tag =>
tag.name -> parseNode(tag)
}
MapNode(subNodes.toMap)
case _ =>
LeafNode(tag.text)
}
http://like-a-boss.net/2012/09/17/variance-in-scala.html#variance_and_type_safety
Variance and type safety
When defining a generic class with a var field we can get compile time errors:
scala> class Invariant[T](var t: T)
defined class Invariant
scala> class Covariant[+T](var t: T)
<console>:7: error: covariant type T occurs in contravariant position in type T of value t_=
class Covariant[+T](var t: T)
^
scala> class Contravariant[-T](var t: T)
<console>:7: error: contravariant type T occurs in covariant position in type => T of method t
class Contravariant[-T](var t: T)
Let’s break it down a little. Why doesn’t the compiler allow getters in the Covariant class?
scala> abstract trait Covariant[+T] {
| def take(t: T): Unit
| }
<console>:8: error: covariant type T occurs in contravariant position in type T of value t
def take(t: T): Unit
^
scala> abstract trait Contravariant[-T] {
| def take(t: T): Unit
| }
defined trait Contravariant
Why? Let’s think about usages of covariance let’s say that we have a class:
class Printer[+T] {
| def print(t: T): Unit = ???
| }
<console>:8: error: covariant type T occurs in contravariant position in type T of value t
def print(t: T): Unit = ???
If the print method can print Dogs does it make sense (in general) that it should also print Animals? Maybe sometimes but in the general sense if we want to generalize the Printer class we should use contravariance. The compiler is smart enough to check this type of usage for us.
Let’s think about the second use case: returning a generic parameter:
scala> class Create[-T] {
| def create: T = ???
| }
<console>:8: error: contravariant type T occurs in covariant position in type => T of method create
def create: T = ???
And again - does it make sense that Create should generalize by contravariance? If Create returns instances of the Animal class should we be able to use it in every place that expects Create[Dog]? The scala compiler is smart enough that it explodes in our face if we try it.
I can make it work this way:
package data
case class Data[+T](val value:T)
{
println(value.getClass)
}
This way I have to explicitly initialize value from the constructor. Nothing wrong with this, just I find it a bit too verbose.
import data.Data
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.Map
object Run extends App
{
val a=Data[ArrayBuffer[Data[Any]]](ArrayBuffer[Data[Any]]())
a.value+=Data[String]("bar")
println(a.value)
val m=Data[Map[String,Data[Any]]](Map[String,Data[Any]]())
m.value+=("foo"->Data[String]("bar"))
println(m.value)
}
This prints:
class scala.collection.mutable.ArrayBuffer
class java.lang.String
ArrayBuffer(Data(bar))
class scala.collection.mutable.HashMap
class java.lang.String
Map(foo -> Data(bar))
The program only compiles with Data having +T type parameter, otherwise I get the error:
type mismatch;
[error] found : data.Data[String]
[error] required: data.Data[Any]
[error] Note: String <: Any, but class Data is invariant in type T.
[error] You may wish to define T as +T instead. (SLS 4.5)
[error] a.value+=Data[String]("bar")