0

I am trying to modify the Enterprise trait that extends the StarShip type.

(I apologize in advance for not knowing a proper title for this but I will update it after I understand the answer.)

For some reason I cannot get the parameters of the starShip that is passed. I commented where I am getting the error.

Simple example to demonstrate this:

object Test {

  trait Akira extends StarShip

  trait Enterprise extends StarShip

  sealed trait StarShip {
    val captain: String
  }

  def doSomething[T: StarShip](starShip: T): T =
    new T {
       val captain = starShip.captain // Error: Cannot resolve symbol
    }

  doSomething(new Enterprise {
    override val captain = "Picard"
  })
}

Since I am passing an object of Enterprise I expect to get the same class out.

Edit: Just realized that I want all variables in the starShip that is passed to be copied to the new class. With the exception that I will modify a few of them.

I believe the Monocle lib may solve my problem.

Chris Martin
  • 28,558
  • 6
  • 66
  • 126
BAR
  • 12,752
  • 18
  • 79
  • 153
  • 4
    Yeah, the Monocle lib may help. I also believe you meant to use a lower bound `T <: bound="" context="" instead="" of="" starship="" the=""> – dcastro Sep 08 '15 at 22:29
  • @dcastro Yes that does work. Although I realized my problem was a bit different. Thanks for this info – BAR Sep 08 '15 at 23:29

1 Answers1

0

When I compile your code with the scala REPL, I get this:

<console>:17: error: Test.StarShip does not take type parameters
     def doSomething[T: StarShip](starShip: T): T =
                          ^
<console>:18: error: class type required but T found
       new T {
               ^
<console>:19: error: value captain is not a member of type parameter T
          val captain = starShip.captain // Error: Cannot resolve symbol
                                     ^
<console>:18: error: type mismatch;
 found   : T{}
 required: T
           new T {
           ^

As @dcastro mentioned, you probably wanted a type bound on T. However, even fixing that syntactical error isn't good enough because:

scala> def doSomething[T <: StarShip](starShip : T) : T = new T { val captain = starShip.captain }
<console>:8: error: class type required but T found

It is not possible to instantiate an object from a type parameter because the compiler does not know the actual type to instantiate until runtime. It could be anything, including a type written after the compilation of this function.

Based on your invocation of doSomething, I don't think you want the type parameter or the instantiation at all. That is, this works:

scala> def doSomething[T <: StarShip](starShip : T) : T = { val captain = starShip.captain; starShip }
doSomething: [T <: StarShip](starShip: T)T

scala> doSomething(new Enterprise { val captain = "Kirk" })
res0: Enterprise = $anon$1@26653222

So, I think with the above adjustments you have accomplished your goal. You've modified your trait (StarShip) by insantiating an Enterprise with an overriden value for the captain member.

Reid Spencer
  • 2,616
  • 25
  • 33
  • I see where you're going with this but it looks like too much was teased apart. The Enterprise needs its other parameters copied as well and only one modified. I do believe it is possible to instantiate T that is not the base Starship because we have passed in the class we want; the Enterprise. – BAR Sep 09 '15 at 12:10
  • It really looks like the right answer will use Monacle. I was knocking out a few more critical tasks so have not gotten to it yet. – BAR Sep 09 '15 at 12:16
  • Also interesting note on the type being unknown at runtime. Isn't this one of the benefits of using sealed traits? All possible subclasses are known at compile time. But say that was not the case, it should still be possible during runtime with JIT. In either case, doesn't the snippet that comes right after invoke doSomething successfully which disproves that? – BAR Sep 09 '15 at 12:36
  • Try printing the final captain result, im away from my mac but it looks like the last snippet does not change the captain. – BAR Sep 09 '15 at 13:49
  • @BAR - You seem to be confused about Scala. The "Enterprise" trait, as you've defined it does not have any additional members (not parameters) and the instantiation of it in the doSomething invocation merely overrides the "captain" value. The language will not allow you to instantiate a type parameter. See this question: http://stackoverflow.com/questions/1305563/how-to-instantiate-an-instance-of-type-represented-by-type-parameter-in-scala – Reid Spencer Sep 09 '15 at 16:25
  • @BAR - I have no idea what Monacle is and google can't find anything useful. If you mean using a lens via Monocle that would just seem to complicate matters. If you've already decided that Monocle is the answer then there isn't much to discuss. Type erasure is a feature of the JVM. You can use reflection or Scala's type support at runtime if you wish to instantiate dynamically. The last snippet does change the value of "captain" to "Kirk". It doesn't print anything because my definition of doSomething is (nonsensically) equivalent to yours. – Reid Spencer Sep 09 '15 at 16:30
  • I meant print the result of 'doSomething(new Enterprise { val captain = "Kirk" }).captain println' sorry I did not mean to be nonsensical. – BAR Sep 09 '15 at 17:24
  • The Scala REPL printed that. doSomething returns a StarShip instance and the REPL printed the anonymous subclass of Enterprise instance and its integer address. – Reid Spencer Sep 09 '15 at 18:13