I see this code in this blog: Type-Level Programming in Scala:
// define the abstract types and bounds
trait Recurse {
type Next <: Recurse
// this is the recursive function definition
type X[R <: Recurse] <: Int
}
// implementation
trait RecurseA extends Recurse {
type Next = RecurseA
// this is the implementation
type X[R <: Recurse] = R#X[R#Next]
}
object Recurse {
// infinite loop
type C = RecurseA#X[RecurseA]
}
There is an operator # in the code R#X[R#Next] which I've never seen. Since it's difficult to search it(ignored by search engines), who can tell me what does it mean?
To explain it, we first have to explain nested classes in Scala. Consider this simple example:
class A {
class B
def f(b: B) = println("Got my B!")
}
Now let's try something with it:
scala> val a1 = new A
a1: A = A#2fa8ecf4
scala> val a2 = new A
a2: A = A#4bed4c8
scala> a2.f(new a1.B)
<console>:11: error: type mismatch;
found : a1.B
required: a2.B
a2.f(new a1.B)
^
When you declare a class inside another class in Scala, you are saying that each instance of that class has such a subclass. In other words, there's no A.B class, but there are a1.B and a2.B classes, and they are different classes, as the error message is telling us above.
If you did not understand that, look up path dependent types.
Now, # makes it possible for you to refer to such nested classes without restricting it to a particular instance. In other words, there's no A.B, but there's A#B, which means a B nested class of any instance of A.
We can see this in work by changing the code above:
class A {
class B
def f(b: B) = println("Got my B!")
def g(b: A#B) = println("Got a B.")
}
And trying it out:
scala> val a1 = new A
a1: A = A#1497b7b1
scala> val a2 = new A
a2: A = A#2607c28c
scala> a2.f(new a1.B)
<console>:11: error: type mismatch;
found : a1.B
required: a2.B
a2.f(new a1.B)
^
scala> a2.g(new a1.B)
Got a B.
It's known as type projection, and is used to access type members.
scala> trait R {
| type A = Int
| }
defined trait R
scala> val x = null.asInstanceOf[R#A]
x: Int = 0
Basically, it's a way of referring to classes within other classes.
http://jim-mcbeath.blogspot.com/2008/09/scala-syntax-primer.html (search for "pound")
Here's a resource for searching on "symbolic operators" (which are really methods), but I haven't figured out how to escape "#" to search on in scalex)
http://www.artima.com/pins1ed/book-index.html#indexanchor
Related
Why can I do the following:
class A
type M[_] = A
I would expect I can only alias type that expects one type parameter, for example a List[_], but it works also with plain classes.
If I create a method:
def foo(m: M[_]) = m
and call it with wrong parameter:
scala> foo("a")
<console>:15: error: type mismatch;
found : String("a")
required: M[_]
(which expands to) A[]
foo("a")
I get such error. What is the meaning of A[]?
Going further, how to explain this:
scala> type M[_, _] = A
<console>:12: error: _ is already defined as type _
type M[_, _] = A
Is there a way to assure that what I put on the right hand side of my alias will be a parametrized type?
type M[_] = A is the same as type M[X] = A: a constant function on types. M[X] is A whatever X is: M[Int] is A, M[String] is A, M[Any] is A, etc. _ in this case is just an identifier (which explains the error for type M[_, _] as well).
Of course, in def foo(m: M[_]) = m, M[_] is an existential type: M[T] forSome { type T }. I don't know why Scala says it expands to A[] in the error message, though; this may be a bug. You can check it's the same type as A by calling
scala> implicitly[M[_] =:= A]
res0: =:=[A[],A] = <function1>
Is there a way to assure that what I put on the right hand side of my alias will be a parametrized type?
You can declare an abstract member type with higher-kind
trait Foo { type M[_] }
and it can only be implemented by parametrized types:
class Bar1 extends Foo { type M = Int } // fails
class Bar2 extends Foo { type M[X] = List[X] } // works
Of course, as mentioned in the first paragraph, M in type M[X] = Int is parametrized, and I don't think there is a way to rule it out.
I'm playing with Scala's type system with the following example:
scala> sealed trait A
defined trait A
scala> case class A1(n: Int) extends A
defined class A1
scala> case class A2(n: Int) extends A
defined class A2
scala> case class MyCase[+P <: A](a: A, y: String)
defined class MyCase
scala> val l = List(MyCase[A1](A1(1), "A1"), MyCase[A2](A2(2), "A2"))
Now, when I do the following:
scala> l.head.asInstanceOf[MyCase[A2]]
res2: MyCase[A2] = MyCase(A1(1),A1)
How can a MyCase[A1] instance be assigned to a MyCase[A2] reference? I mean MyCase[A1] and MyCase[A2] are at the same level in the object hierarchy!
As I understand you're wondering why you didn't get an error here in runtime, when you did l.head.asInstanceOf[MyCase[A2]]. So the answer is simple - type erasure. For JVM both types are equal as it ignores generic parameter - it simply doesn't know about them as they exist on compiler level only, so only compiler can give you an exception:
scala> val mcA2: MyCase[A2] = l.head
<console>:15: error: type mismatch;
found : MyCase[Product with Serializable with A]
required: MyCase[A2]
val mcA2: MyCase[A2] = l.head
^
However, asInstanceOf ignores this check. So when you do
case class MyCase[+P <: A](a: P) //P instead of A
scala> val k = MyCase[A1](A1(0)).asInstanceOf[MyCase[A2]]
k: MyCase[A2] = MyCase(A1(0))
scala> val a: A2 = k.a
java.lang.ClassCastException: A1 cannot be cast to A2
... 33 elided
But in your example - you never really used P in runtime (and it's impossible), so you never gonna get ClassCastException. Actually, under these conditions (no real instances), you can do type-level copy like here, but it safer to use traits for that.
This question already has an answer here:
Attempting to model F-bounded polymorphism as a type member in Scala
(1 answer)
Closed 8 years ago.
I want to convert F bounded polymorphism to abstract type members.
trait FBoundedMovable[Self <: FBoundedMovable[Self]] {
def moveTo(pos: Vect2): Self
}
to
trait Movable { self =>
type Self <: (Movable { type Self = self.Self })
def moveTo(pos: Vect2): Self
}
So far so good.
Lets define an instance:
case class Ship(pos: Vect2) extends Movable {
type Self = Ship
def moveTo(pos: Vect2) = copy(pos = pos)
}
And try to use it:
// [error] found : a.Self
// [error] required: A
def move[A <: Movable](a: A, to: Vect2): A = a.moveTo(to)
F bounded version works fine.
def moveF[A <: FBoundedMovable[A]](a: A, to: Vect2): A = a.moveTo(to)
I know it's possible to add type bounds to method definition site:
def move2[A <: Movable { type Self = A }](a: A, to: Vect2): A = a.moveTo(to)
But is it possible to specify the relationship in Movable trait declaration? If not - why?
Edit 1
I've realised what problem I am having.
Lets say we want to declare that something is a unit in our world.
trait WorldUnit extends Movable with Damageable
All units are movable and damagable.
Our combat calculation stuff only cares that stuff is Movable with Damagable. It doesn't care whether it is unit or building.
However we can have code like this:
def doCombat(obj: Movable with Damagable) = obj.moveTo(...).takeDamage(...)
def doStuffWithUnit(obj: WorldUnit): WorldUnit = doCombat(obj) // and the type is lost here.
Am I doomed for F bounded types?
Edit 2:
The question is not answered by Attempting to model F-bounded polymorphism as a type member in Scala - I've tried that before and it doesn't impact the return type in a slightest, it's still a.Self.
Edit 3:
I've found http://blog.jessitron.com/2014/02/when-oo-and-fp-meet-mytype-problem.html but a problem is still unresolved.
Basically, whenever you have a collection and want to pick one:
(collection: Seq[Movable]).collectFirst { m: Movable if m.someCondition => m } - you have no way of specifying the type bound, thus compiler cannot prove that A#Self =:= A?
Type-Projections in scala are path-dependent. Quick example
scala> trait A{
| type T
| }
defined trait A
scala> val a = new A{type T = String}
a: A{type T = String} = $anon$1#31198ceb
scala> val b = new A{type T = String}
b: A{type T = String} = $anon$1#236ab296
scala> def f(implicit evidence: A =:= b.T) = null
f: (implicit evidence: =:=[A,b.T])Null
scala> f("asdf":a.T)
<console>:12: error: type mismatch;
found : a.T
(which expands to) String
required: =:=[A,b.T]
(which expands to) =:=[A,String]
f("asdf":a.T)
^
In your case, it throws an error because of the return type. It rightly expects a.type but you return A. And they are not the same.
The reason why they shouldn't be the same is:
a.type returns a type <: Movable. For imagination, some number x less than 100. Method move returns A and for imagination, it is some another number y less than 100. Its not necessary that x should be same as y.
I have the following case classes:
object Fields {
abstract class Base{
def getString: String
}
case class A() extends Base{
def getString = "A+++"
}
case class B() extends Base{
def getString = "B++"
}
def fieldsToString(fields: List[Base]): String = {
fields.tail.foldLeft(s"${fields.head.getString}") {(acc, f) =>
acc + s",${f.getString}"
}
}
}
Then I tried to call this function in the following way:
val fields = List(A, B, A)
val result = Fields.fieldsToString(fields)
which gives me the following error:
type mismatch;
[error] found : List[scala.runtime.AbstractFunction0[Product with Serializable
with Fields.Base] with Serializable]
So I thought I needed to introduce covariance:
def fieldsToString[T >: Base](fields: List[T]): String = {
fields.tail.foldLeft(s"${fields.head.getString}") {(acc, f) =>
acc + s",${f.getString}"
}
}
Which then gives me the following compile error:
do not conform to method fieldsToString's type parameter bounds [T <: Fields.Base]
What exactly is the problem, is this due to the case classes, do they behave differently then normal classes?
Rightfold answer was close, what you have are not really functions but singleton types:
scala> case class A()
defined class A
scala> A
res0: A.type = A
scala> res0()
res1: A = A()
Quoting the scala overview paper (on page 9, right column) by Odersky when talking about abstract members:
Here, p.type is a singleton type, which represents just the object denoted by p. Singleton types by themselves are also useful for supporting chaining of method calls.
If you look closely at the first error message, you see that you have created a list of functions. Call them:
val fields = List(A(), B(), A())
This is due to case classes implicitly having companion objects with apply methods.
Let's say I have this trait
trait Ctx[C, V[_]]
I am unable to construct any method signature that takes a Ctx of which the second type parameter is unspecified (wildcard). E.g. this:
def test(c: Ctx[_, _]) = ()
doesn't compile ("error: _$2 takes no type parameters, expected: one"). Neither can I do
def test(c: Ctx[_, _[_]]) = ()
("error: _$2 does not take type parameters"). What am I missing?
I'm able to define this one:
def test[V[X]](c:Ctx[_,V]) {}
And it seems to work ok with type inference:
scala> trait Ctx[ C, V[ _ ]]
defined trait Ctx
scala> def test[V[X]](c:Ctx[_,V]) {}
test: [V[X]](c: Ctx[_, V])Unit
scala> class P extends Ctx[Int, List]
defined class P
scala> new P
res0: P = P#1f49969
scala> test(res0)
Edit: I suspect it won't be practical to replace Ctx to use an abstract type, but this is what I was able to do:
trait Ctx[C] { type V[X] }
class CtxOption[C] extends Ctx[C] { type V[X] = Option[X] }
class CtxList[C] extends Ctx[C] { type V[X] = List[X] }
def test(ctx:Ctx[_]) { println(ctx) }
val ctxOptInt = new CtxOption[Int]
val ctxListStr = new CtxList[String]
test(ctxOptInt)
test(ctxListStr)
val list = collection.mutable.ListBuffer[Ctx[_]]()
list += ctxOptInt
list += ctxListStr
list
Using an abstract type for V spares you the complicated (or impossible) task of figuring the type parameter syntax for a wildcard type constructor. Additionally as demonstrated in the ListBuffer example you can then handle objects where the V is a different type constructor (Option and List in my example). The first solution I provided would not allow you to do that.
Edit 2: How about?
trait AbstractCtx[C] { type W[X] }
trait Ctx[C,V[_]] extends AbstractCtx[C] { type W[X] = V[X] }
def test(ctx:AbstractCtx[_]) { println(ctx) }
You need to pass a type constructor for the second argument of Ctx. Scala is not able to infer the correct kind if you just pass _. Neither is it possible to define a type constructor with wildcards (i.e. _[_]] on the fly. Note that in your first example _$2 in the error message refers to the type passed as second argument to Ctx as a whole. In the second example however _$2 refers to the the first wildcard type in _[_]. See the location indicator in the error messages:
<console>:6: error: _$2 does not take type parameters
def test( c: Ctx[ _, _[ _ ]]) {}
^
The following works since here V is a type constructor of the right kind expected by Ctx.
def test[V[_]]( c: Ctx[ _, V]) {}