Type parameter not working

Hi,
I’m new to using Scala and am trying to see if a list contains any objects of a certain type.

When I make a method to do this, I get the following results:

var l = List("Some string", 3)
def containsType[T] = l.exists(_.isInstanceOf[T])
containsType[Boolean]   // val res0: Boolean = true
l.exists(_.isInstanceOf[Boolean])   // val res1: Boolean = false

Could someone please help me understand why my method doesn’t return the same results as the expression on the last line?

Thank you,
Johan

Probably related to type erasure.
In any case, methods like containsType and things like List[Any] are, usually, a result of a bad design- And may not work as expected due to things like type erasure, isInstanceOf check classes, not types; they are different things (and types do not longer exist at runtime which is when this check will be executed)

Can I ask, what is the meta-problem you are trying to solve?
Or are you simply playing with code?
Or maybe you come from a dynamic background like Python or JS?

Hi Balmung,

I come from developing game in Godot with GDscript (it’s like Python) and making games in Unity with C# (long time ago).

I’m trying to make a small game engine using Scala. In the program I have a class GameObjects that contains a list of GameObjectComponent instances that the game engine wil use for things like physics simulation (e.g. if a game object has a Rigidbody component the Physics engine will process that game object). Here’s what GameObject looks like:

import GameEnjin.Geometry.Vector2

class GameObject(var name: String) {
  var position: Vector2 = Vector2(0, 0)
  var components: List[GameObjectComponent] = List.empty

  def addComponent(c: GameObjectComponent) = components = components :+ c
  def hasComponent[T <: GameObjectComponent]: Boolean = components.exists(_.isInstanceOf[T])
  def getComponent[T <: GameObjectComponent]: T =
    val r = getComponents[T]
    r.head
  def getComponents[T <: GameObjectComponent]: List[T] = components.filter(_.isInstanceOf[T]).map(_.asInstanceOf[T])
}

So the question I asked relates to the hasComponent method on the GameObject class in the above code. I want to be able to check if a game object has a certain component on it, like the Rigidbody component, or a Sprite component etc.

Thank you so much for your help.

Kind regards

Here’s the code that uses the hasComponent check:

import GameEnjin.GameObject

class Physics {
  def step(gameObjects: List[GameObject], delta: Float): Unit =
  // Mutates the state of the physicsObjects
    for (o <- gameObjects)
      if (o.hasComponent[PhysicsObject])
        val p = o.getComponent[PhysicsObject]
        o.position += (p.velocity * delta)
}

First, do you really need all that mutability? Have you even taken the time to learn the language (not the syntax) and its patterns?

Anyways, if GameObjectComponent is an ADT, you can use pattern matching instead of checking by class; but you probably don’t know what that is.
You may also use a typeclass or follow the type value design.

But, I would just say that if you do [T <: GameObjectComponent : ClassTag] it should work (as long as you don’t add generic) and that is probably all you care.

I finished a course in Scala (Principle of Functional Programming in Scala) on Coursera about 5 years ago and I really liked the language. I wanted to create a small game engine within a couple of weeks, and thought that it would be awesome to try using Scala for it. So I don’t really have much experience building applications in Scala.

I was just want to understand why this works:

l.exists(_.isInstanceOf[Boolean])   // val res1: Boolean = false

but this doesn’t:

def containsType[T] = l.exists(_.isInstanceOf[T])
containsType[Boolean]   // val res0: Boolean = true

It doesn’t make sense to me, but I’m assuming it has to do with “type erasure” as you mentioned. I’ll try to look into it and see if I can understand what’s going on, thank you.

Also, I tried using this:

but I had the same results:

var l = List("Some string", 3)
def containsType[T : ClassTag] = l.exists(_.isInstanceOf[T])
containsType[Boolean]   // val res0: Boolean = true
l.exists(_.isInstanceOf[Boolean])   // val res1: Boolean = false

Thank you for trying to help anyhow, hope you have a good day.

Kind regards

Right, my bad, it should be like this:

def getComponents[T <: GameObjectComponent : ClassTag]: List[T] = 
  components.collect {
    case t: T => t
  }
2 Likes

Thank you!

I’ve learned a lot about how scala works today and am trying to let it all sink in for the future.

Thanks for you help.

That’s great! In addition to what BalmungSan said, I’d like to add a few things:

var l = List("Some string", 3)

Typically you don’t want to use var in Scala (unless it’s absolutely necessary), but use val instead.

Second, you normally don’t mix different types of elements in the same list in Scala (I can understand if you are coming from dynamic languages). I tried re-writing your function with cases:

scala> def containsType[T] = list.exists { 
     |   case _: T => true 
     |   case _ => false
     | }
1 warning found
def containsType[T] => Boolean
-- Unchecked Warning: -----------------------------------------------------------------------------------------------------------------
2 |  case _: T => true 
  |       ^
  |       the type test for T cannot be checked at runtime

In addition to the type erasure issues like above, mixing types in the same list “widens” the type (to Any in Scala 2, to Matchable in Scala 3):

scala> val list = List("Some String", 3)
val list: List[Matchable] = List(Some String, 3)

so you lose some of the advantages of type checking and variance. Try to keep collections restricted to one type.

def containsType[T] = l.exists(_.isInstanceOf[T])

isInstanceOf and asInstanceOf are discouraged and considered poor Scala style. Here is an excerpt from “Programming in Scala 5th Edition”:
poor

You should use pattern matching case ... => as BalmungSan showed in his solution. In particular you can use “typed patterns” like BalmungSan showed: case t: T => ...

1 Like

Yet another remark… Checking for presence and extracting a GameObjectComponent might be encoded via the Option data type.

def getComponent[T <: GameObjectComponent : ClassTag]: Option[T] =
  components.collectFirst { case c: T => c }

With this, you could rephrase your #step() method:

def step(gameObjects: List[GameObject], delta: Float): Unit =
  for {
    o <- gameObjects
    p <- o.getComponent[PhysicsObject]
  } o.position += (p.velocity * delta)

…where the for expression desugars to nested #foreach() invocations for the game object List and the physics object Option, respectively.

1 Like

When you compiled this code:

def containsType[T] = l.exists(_.isInstanceOf[T])

You should have seen this warning:

warning: abstract type T is unchecked since it is eliminated by erasure

This warning isn’t some stylistic nitpick it’s safe to ignore; it’s telling you that this code is wrong and will not operate as intended. Arguably the compiler ought to issue an error here, not merely a warning.

I hesitate to offer the following, because you’re still learning Scala and what I’m about to say is fairly advanced Scala, not beginning Scala. Part of learning a new language is learning which coding patterns are considered usual and normal and which coding patterns are considered something you reach for only in unusual situations or as a last resort.

That said, it is possible to define something like what you wanted by requiring a ClassTag:

def containsClass[T : reflect.ClassTag] =
  l.exists{case x: T => true; case _ => false}

Note that I changed from isInstanceOf to a pattern match because pattern matching has special support for implicit ClassTags but isInstanceOf does not. (Normal Scala code rarely uses isInstanceOf at all.)

Also note that I changed the method name because we are doing a compile-time type test here. We’re doing a runtime check on what class something is, which is the most we can do, because types are a compile-time concept; full type information doesn’t exist at runtime.

So now I can:

scala> containsClass[Boolean]
val res0: Boolean = false

scala> containsClass[String]
val res1: Boolean = true

But note that this won’t warn us if we provide a more specific type than can actually be checked:

scala> containsClass[List[Int]]
val res5: Boolean = true

scala> containsClass[List[String]]
val res6: Boolean = true

where the latter result isn’t as intended and isn’t even giving us a warning.

Anyway, I’m not going to launch into a full explanation of all this at the moment. And I repeat that this is runtime reflection, not normal Scala, and we don’t normally program this way in Scala – certainly not when we’re just learning the language.

Statically typed languages and dynamic languages are really deeply, fundamentally different from each other and this deep difference results in different coding patterns and different ways of thinking about code.

2 Likes

In this case the intent likely is to implement an Entity Component System, i.e. the pattern is more rooted in the tradition of the domain (generic game engines) than in a specific programming paradigm.

1 Like