1

I am experimenting with type variables and structural types as part of learning Scala's 'cake' pattern.

Below is a toy API that illustrates my question:

When methods defined in structurual type 'B' are a superset of those in structurual type 'A', why can't I pass an instance of B to a method that wants an 'A'?

...  Toy API ....

object Tester extends App {

  trait SomeApi {
    type Organism <: {
      def die(): Unit;
    }

    type Dog <: {
      def die(): Unit;
      def bark(): Unit;
    }

    def dieQuietlyDoesntCompile(entity: Organism): Unit = {
      entity.die()
    }

   def dieQuietly(entity: { def die(): Unit }): Unit = {
      entity.die()
   }

    def processDog(dog: Dog): Unit = {
      println("start dog process on : " + dog)
      dieQuietly(dog)                            
    }
  }
}

The structural types in my API start off with what you might call a 'base type' (Organism in the example above), plus, I have other types in the API which extend the base type. In the case of the Toy API, Dog has all the methods of Organism, plus one more: 'bark()'.

I want to write some helper methods that operate on the base types as is illustrated by the processDog() method .... which takes a 'Dog' instance, but which also wants to call out to 'dieQuietly' which handles the more generic type 'Organism'.

The way I have done things above works, however it is really clunky because I have to fully repeat all the methods of the base structural type. Not so bad in this toy case (since I only have one method: die()), but really awkward as the number of methods in these structural types increases.

Therefore, I would rather passs the dog instance to a method written like 'dieQuietlyDoesntCompile()'.
But as that functions name indicates, if i pass it a Dog instance, it fails to compile with the error:

type mismatch; found : dog.type (with underlying type SomeApi.this.Dog) required: SomeApi.this.Organism

Can anyone suggest a more convenient way to accomplish my goal...? Or am I stuck repeating the methods in the base type ? (an approach which doesn't seem very DRY to me). Thanks in advance for your help ! /chris

Chris Bedford
  • 2,724
  • 2
  • 23
  • 47

2 Answers2

2

Your problem is that you use type bound <: to define Dog and Organism. You are constraining them as subclasses of class with die() method, which makes them non-related.

Let me illustrate it using regular types:

trait Mortal // suppose this trait is analogue of { def die():Unit }
class Organism extends Mortal
class Dog extends  Mortal

def die(o:Organism) {}

die(new Dog)  // obviously will not compile

Your code can be easily fixed by defining Organism without type bounds:

  type Organism = {
    def die(): Unit;
  }
Aivean
  • 8,465
  • 20
  • 26
  • good points. unfortunately, i need to figure out how to work with structural types not classes, as i am programming against an existing code base. This code base is actually from the coursera class on Akka. Not sure how long the link will last, but here it is in full: https://github.com/tonyskn/coursera-reactive/blob/master/w4-suggestions/src/main/scala/suggestions/gui/SwingApi.scala -- and here is another S.O. question on that same code: http://stackoverflow.com/questions/20508529/subtype-in-scala-what-is-type-x-y – Chris Bedford Oct 23 '15 at 06:10
  • @ChrisBedford, I think I answered your questions. `why can't I pass an instance of B to a method that wants an 'A'?` < because A and B are not related. `Can anyone suggest a more convenient way to accomplish my goal` < see easy fix to `Organism` in my answer. If you want to know something particular about your code snippet on github, then ask. I completed this course several years ago, maybe I will be able to recall something. – Aivean Oct 23 '15 at 06:34
0

To expand a bit on Aivean's answer:

When methods defined in structurual type 'B' are a superset of those in structurual type 'A', why can't I pass an instance of B to a method that wants an 'A'?

You can, but in this case they aren't. Organism isn't a structural type, it's an abstract type member which is required to be a subtype of a structural type. One possible implementation of SomeApi is

class ApiImpl extends SomeApi {

  type Organism = {
    def die(): Unit
    def live(): Unit
  }

  type Dog = {
    def die(): Unit
    def bark(): Unit
  }

  override def dieQuietlyDoesntCompile(entity: Organism): Unit = {
    entity.live()
  }
}

Hopefully it's obvious why you can't pass a Dog to dieQuietlyDoesntCompile here; but this also means you can't pass it in the original case, or you could do

 val api: SomeApi = new ApiImpl

 val dog: api.Dog = ...

 api.dieQuietlyDoesntCompile(dog)
Alexey Romanov
  • 154,018
  • 31
  • 276
  • 433