Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the type of a nested object in scala

I am trying to figure out how to create a method that takes a nested object as an argument. For a nested class I can do the following:

scala> class Outer { 
 | class Inner
 | def method(i:Outer#Inner) = { "inner class" }
 | }
defined class Outer

However, if I try something like that with an object instead i get an error:

scala> class Outer { 
 | object Inner
 | def method(i:Outer#Inner) = { "inner object" }
 | }
<console>:11: error: type Inner is not a member of Outer
   def method(i:Outer#Inner) = { "inner object" }

What should the type of the argument to the method be to accomplish this? Also I want to refer to the type of Inner object not generalize the argument to say Any.

like image 291
Emil H Avatar asked Feb 10 '12 07:02

Emil H


2 Answers

Inneris an object, thus it is not a type, and can't be use as a type. the type of Inner is Inner.type. It means, in your example. Unfortunately, each instance of Outer will have it's own Inner object and type Outer#Inner.type can not be used, since it is not stable. A workaround is to use: this.Inner.type.

 def method(i:this.Inner.type) = { "inner object" }

But it means that you can only pass as a parameter the Inner object of the instance on which you call method.

like image 105
Nicolas Avatar answered Sep 20 '22 04:09

Nicolas


A simple example to illustrate what's going on here (in the REPL):

object A
def foo(a : A) = "Does not compile"
def bar(a : A.type) = "Does compile!"
bar(A) // returns "Does compile!"

As Nicholas says, Inner is not a type, so you can't use it as such.

Trying to understand your motivation, I came up with something like this:

class Outer(i : Int) {
  object Inner {
    def getI : Int = i
  }
  def foo(x : Inner.type) = x.getI
}

This is a bit pointless, since we would just directly reference Inner - after all, there's only one of it:

class Outer(i : Int) {
  object Inner {
    def getI : Int = i
  }
  def foo = Inner.getI
}

I'm guessing that what you want to do is accept an Inner from any instance of Outer. We can check the type of such a thing:

val o = new Outer(1)
:type o.Inner
o.Inner.type

So we might expect to be able to do something like this:

class Outer(i : Int) {
  object Inner {
    def getI : Int = i
  }
  def foo(x : Outer#Inner.type) = x.getI
}

This, however, fails to compile. I'm not sure why. Type aliases to the rescue!

class Outer(i : Int) {
  type Inner = Inner.type
  object Inner {
    def getI : Int = i
  }
  def foo(x : Outer#Inner) = x.getI
}

val a = new Outer(1)
val b = new Outer(2)
a.foo(b.Inner) //returns 2

I'm guessing it's just a restriction of the parser that it's unable to read something of the form A#B.type. You could potentially submit a bug request.

like image 31
Submonoid Avatar answered Sep 17 '22 04:09

Submonoid