scala's type checker doesn't recognize types in abstract path-dependent classes scenario - scala

Let's define a trait with an abstract class
object Outer {
trait X {
type T
val empty: T
}
Now we can make an instance of it:
val x = new X {
type T = Int
val empty = 42
}
Scala now recognizes, that x.empty is an Int:
def xEmptyIsInt = x.empty: Int
Now, let's define an other class
case class Y(x: X) extends X {
type T = x.T
val empty = x.empty
}
and make an instance of it
val y = Y(x)
But now Scala, isn't able to infer that y.empty is of type Int. The following
def yEmptyIsInt = y.empty: Int
now produces an error message:
error: type mismatch;
found : y.x.T
required: Int
y.empty : Int
Why is this the case? Is this a scala bug?
We can mitigate this issue by introducing a parameters to the case class:
case class Z[U](x: X { type T = U }) extends X {
type T = U
val empty = x.empty
}
Then it works again
val z = Z(x)
def zEmptyIsInt: Int = z.empty
But we always have to mention all the types inside X at call-site. This ideally should be an implementation detail which leads to the following approach:
case class A[U <: X](z: U) extends X {
type T = z.T
val empty = z.empty
}
This also mitigates the issue
val a = A(x)
def aEmptyIsInt: Int = a.empty
}
So, to summarize, my questions are the following: Why does the simple case doesn't work? Is this a valid workaround? What other problems might come up when we follow one of the two workaround approaches? Is there a better approach?

You've re-used x for different things, so from here on I'll call the object instantiated by val x "instance x" and the x: X used in class Y "parameter x".
"Instance x" is an anonymous subclass of trait X with concrete members overriding trait X's abstract members. As a subclass of X you can pass it to the constructor for case class Y and it will be accepted happily, since as a subclass it is an X.
It seems to me you expect that case class Y will then check at runtime to see if the instance of X it is passed has overridden X's members, and generate an instance of Y whose members have different types depending on what was passed in.
This is emphatically not how Scala works, and would pretty much defeat the purpose of its (static) type system. For example, you wouldn't be able to do anything useful with Y.empty without runtime reflection since it could have any type at all, and at that point you're better off just using a dynamic type system. If you want the benefits of parametricity, free theorems, not needing reflection, etc. then you have to stick to statically determined types (with a small exception for pattern matching). And that's what Scala does here.
What actually happens is that you've told Y's constructor that parameter x is an X, and so the compiler statically determines that x.empty, has the type of X.empty, which is abstract type T. Scala's inference isn't failing; your types are actually mismatched.
Separately, this doesn't really have anything to do with path-dependent types. Here is a good walkthrough, but in short, path-dependent types are bound to their parent's instance, not determined dynamically at runtime.

Related

Scala: "Static values" in traits?

Let's say I have:
trait X {
val x: String
}
Using mix-in, I can define a trait such as
trait XPrinter {
self: X =>
def printX: String = "X is: " + x
}
such that a value/object implementing XPrinter implements x and give its methods such as printX access to the values specified in X such as x.
So far, so good.
I want to know if there is a way of having a trait in the form of:
trait XDependent[T <: X] {
def printX: String = ???
}
So that XDependent instances have access to the value of T.x, with x assumed to be a "static value" glued with the type definition.
Now I understand why T.x can't be accessed in XDependent since a type subtyping X doesn't even have to implement the value of x and T.x might be abstract.
I understand that while Scala offers path-dependent types so that an abstract type defined in X can be used in XDependent, as shown here:
trait X {
type Y //which can be constrained as desired.
}
trait XDependent[T <: X]{
def foo(v:T#Y)
def bar: T#Y
}
it doesn't offer the same thing with values as there is a clear separation between types and values in Scala.
Now I have come across the ideas of value-dependent types and literal-based types. I want to know if the idea of "static value for types", as illustrated above, has much overlap with the these concepts and what the connections are.
I'd also like to know about the different approaches taken in different languages, to blur the separation between types and values, how compatible they are with Scala's type system, and what the complications are in terms of integrating "static values" with the type-system. ie, (Can they be)/ (what happens if they are) overriden by a subtype, etc.
If you can relax the requirement that XDependent has to be a trait, and make it an abstract class instead, then it seems as if a typeclass which provides a single null-ary method x is exactly what you want:
Here is your base trait X (without X.x or anything, that wouldn't be "static"):
trait X
Now you can define a typeclass HasStaticX[T] that guarantees that for a type T we can give some string x:
trait HasStaticX[T] {
def x: String
}
Then you can use it like this:
abstract class XDependent[T <: X : HasStaticX] {
def printX: String = implicitly[HasStaticX[T]].x
}
What HasStaticX does is essentially building a compile-time partial function that can take type T and produce a string-value x associated with T. So, in a way, it's something like a function that takes types and returns values. If this is what you want, then nothing has to be done to for "integrating static values", it just works in the current non-experimental mainstream versions of Scala.
The "value-dependent types" would be exactly the other way round: those would be essentially "functions" that assign types to values.

Scala get classtag from class instance

I need to write a generic method to get all fields of an object and it's value, the class of this object may contains ClassTag, so we should find a way to get it as well, is any way good way ? the difficulty is we don't know the class ahead, it may contains ClassTag (zero to many), It may not.
For example,
class A(x : Int) {}
a = new A(1)
We should output x => 1
class B[T: ClassTag]() {}
b = new B[Float]()
We should output _$1 = Float
def fields(obj: AnyRef) = obj.getClass.getDeclaredFields.map(field => (field.getName, field.get(obj))
will give you an array of pairs of field names and corresponding values, which you can massage into the format you want. You can test for types and do something depending on whether you have a ClassTag or not.
But for your specific examples: neither x in A nor the ClassTag in B are fields, they are just constructor parameters which aren't stored anywhere in the instance. To change this, you can declare it as a val:
class A(private val x: Int)
class B[T]()(private val tag: ClassTag[T])
or make sure they are used somewhere in the body outside the constructor.

Missing scodec.Codec[Command] implicit because of class with non-value fields

I'm trying to use discriminators in existing project and something is wrong with my classes I guess.
Consider this scodec example. If I change TurnLeft and its codec to
sealed class TurnLeft(degrees: Int) extends Command {
def getDegrees: Int = degrees
}
implicit val leftCodec: Codec[TurnLeft] = uint8or16.xmap[TurnLeft](v => new TurnLeft(v), _.getDegrees)
I get
Error:(x, x) could not find Lazy implicit value of type scodec.Codec[Command]
val codec: Codec[Either[UnrecognizedCommand, Command]] = discriminatorFallback(unrecognizedCodec, Codec[Command])
It all works if I make degrees field value field. I suspect it's something tricky with shapeless. What should I do to make it work ?
Sample project that demonstrates the issue is here.
shapeless's Generic is defined for "case-class-like" types. To a first approximation, a case-class-like type is one whose values can be deconstructed to it's constructor parameters which can then be used to reconstruct an equal value, ie.
case class Foo ...
val foo = Foo(...)
val fooGen = Generic[Foo]
assert(fooGen.from(fooGen.to(foo)) == foo)
Case classes with a single constructor parameter list meet this criterion, whereas classes which don't have public (lazy) vals for their constructor parameters, or a companion with a matching apply/unapply, do not.
The implementation of Generic is fairly permissive, and will treat (lazy) val members which correspond to constructor parameters (by type and order) as being equivalent to accessible constructor arguments, so the closest to your example that we can get would be something like this,
sealed class TurnLeft(degrees: Int) extends Command {
val getDegrees: Int = degrees
}
scala> Generic[TurnLeft]
res0: shapeless.Generic[TurnLeft]{type Repr = Int :: HNil } = ...
In this case getDegrees is treated as the accessor for the single Int constructor parameter.

What's the rule to implement an method in trait?

I defined a trait:
trait A {
def hello(name:Any):Any
}
Then define a class X to implement it:
class X extends A {
def hello(name:Any): Any = {}
}
It compiled. Then I change the return type in the subclass:
class X extends A {
def hello(name:Any): String = "hello"
}
It also compiled. Then change the parameter type:
class X extends A {
def hello(name:String): Any = {}
}
It can't compiled this time, the error is:
error: class X needs to be abstract, since method hello in trait A of type (name: Any)
Any is not defined
(Note that Any does not match String: class String in package lang is a subclass
of class Any in package scala, but method parameter types must match exactly.)
It seems the parameter should match exactly, but the return type can be a subtype in subclass?
Update: #Mik378, thanks for your answer, but why the following example can't work? I think it doesn't break Liskov:
trait A {
def hello(name:String):Any
}
class X extends A {
def hello(name:Any): Any = {}
}
It's exactly like in Java, to keep Liskov Substitution principle, you can't override a method with a more finegrained parameter.
Indeed, what if your code deals with the A type, referencing an X type under the hood.
According to A, you can pass Any type you want, but B would allow only String.
Therefore => BOOM
Logically, with the same reasonning, a more finegrained return type is allowed since it would be cover whatever the case is by any code dealing with the A class.
You may want to check those parts:
http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Covariant_method_return_type
and
http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Contravariant_method_argument_type
UPDATE----------------
trait A {
def hello(name:String):Any
}
class X extends A {
def hello(name:Any): Any = {}
}
It would act as a perfect overloading, not an overriding.
In Scala, it's possible to have methods with the same name but different parameters:
class X extends A {
def hello(name:String) = "String"
def hello(name:Any) = "Any"
}
This is called method overloading, and mirrors the semantics of Java (although my example is unusual - normally overloaded methods would do roughly the same thing, but with different combinations of parameters).
Your code doesn't compile, because parameter types need to match exactly for overriding to work. Otherwise, it interprets your method as a new method with different parameter types.
However, there is no facility within Scala or Java to allow overloading of return types - overloading only depends on the name and parameter types. With return type overloading it would be impossible to determine which overloaded variant to use in all but the simplest of cases:
class X extends A {
def hello: Any = "World"
def hello: String = "Hello"
def doSomething = {
println(hello.toString) // which hello do we call???
}
}
This is why your first example compiles with no problem - there is no ambiguity about which method you are implementing.
Note for JVM pedants - technically, the JVM does distinguish between methods with different return types. However, Java and Scala are careful to only use this facility as an optimisation, and it is not reflected in the semantics of Java or Scala.
This is off the top of my head, but basically for X.hello to fit the requirements of A.hello, you need for the input of X.hello to be a superclass of A.hello's input (covariance) and for the output of X.hello to be a subclass of A.hello's output(contravariance).
Think of this is a specific case of the following
class A
class A' extends A
class B
class B' extends B
f :: A' -> B
g :: A -> B'
the question is "Can I replace f with g in an expression y=f(x) and still typecheck in the same situations?
In that expression, y is of type B and x is of type A'
In y=f(x) we know that y is of type B and x is of type A'
g(x) is still fine because x is of type A' (thus of type A)
y=g(x) is still fine because g(x) is of type B' (thus of type B)
Actually the easiest way to see this is that y is a subtype of B (i.e. implements at least B), meaning that you can use y as a value of type B. You have to do some mental gymnastics in the other thing.
(I just remember that it's one direction on the input, another on the output, and try it out... it becomes obvious if you think about it).

Type alias for mix-in type

Since I can do this:
case class A(a: Int)
trait C
val x = new A(10) with C
Why can't I do this:
type X = A with C
val x = new X(10)
? If I can't even construct an instance, what's the use case of type X = A with C?
The error message that you get should give you a hint:
error: class type required but A with C found
new X(10)
^
X, as a type alias, gets rewritten to an A with C type expression, which is not a class type. The latter, according to the Scala Language Specification, is:
a type designator
(ยง
3.2.3
) that refers to a a class or a trait
(emphasis mine)
In other words, it's not every type expression.
This is not an authoritative answer, but I don't believe it's possible to define a type alias in such a way that it becomes a class type. On the one hand, a new expression theoretically accepts an AnnotType, as defined in Section 5.1.1. However, I don't see how, using the type alias grammar from Section 4.3, you could specify what constructor are you using etc..
tl;dr - unless your type alias is directly rewritable to a class type (e.g. A in your example), you can't use it as a class type, which includes invoking new with it. If you want that, you need a class declaration, i.e. class X(a: Int) extends A(a) with C.
Regarding your second question, you say you can't instantiate X. Ah, but that's where you're wrong! Let me show you an example, based on your code:
def blah(x: X) = x.toString
val x = new A(10) with C
val y = new A(10)
blah(x) //String = A(10)
blah(y) //type error
So, it's useful whenever you need a type constraint, since the "aliased" type will be matched to the type alias, even if it wasn't explicitly declared as such.