1

In the Coursera course "Principles of Reactive Programming", Odersky introduces this snippet:

trait Generator[+T] {
    self => // an alias for ”this”.
    def generate: T
    def map[S](f: T => S): Generator[S] = new Generator[S] {
        def generate = f(self.generate)
    }
    def flatMap[S](f: T => Generator[S]): Generator[S] = new Generator[S] {
       def generate = f(self.generate).generate
    }
}

I don't quite recognise the self => thing though. Why does it work and why wouldn't I just do val self = this ?

0__
  • 66,707
  • 21
  • 171
  • 266
BasilTomato
  • 1,071
  • 1
  • 8
  • 14

1 Answers1

2

The basic mechanism is treated in this question. The problem that is addressed here is that both in map and flatMap you need to reference the outer Generator from the newly created inner Generator.

If you would write this here instead of self:

def map[S](f: T => S): Generator[S] = new Generator[S] {
  def generate = f(this.generate) // wrong!
}

you would not identify the outer but the inner instance. this always refers to the "closest" class instance seen from the spot where you use it.

Similarly, if you added a method def self = this to Generator, you then call again the wrong self in the inner scope (the new generator has its own self method now).


The alias in comparison is not a method, it doesn't exist "twice" in the code example so to speak, so there is no shadowing happening when you write new Generator. This is just a more cleaned up way for

def map[S](f: T => S): Generator[S] = {
  val outer = this  // capture outer instance, since `this` will change meaning
  new Generator[S] {
    def generate = f(outer.generate)  // refer to the outer instance
  }
}
0__
  • 66,707
  • 21
  • 171
  • 266