Epicurist
Epicurist

Reputation: 923

Referencing instance member from anonymous function

I'm trying to define a class whose instances have a String and a function. In the function the String parameter is used.

class Tenant(val name: String, exclusion: Map[String, Int] => Boolean)

val rule1 = new Tenant(name = "Baker3",
  (suggestedFloor: Map[String, Int]) => suggestedFloor(name) != topFloor)

val rule1 = new Tenant(name = "Cooper2",
  (suggestedFloor: Map[String, Int]) => suggestedFloor(name) != groundFloor)

Which gives an error on the last use of name: not found: value name.

How can I do that?

Upvotes: 0

Views: 87

Answers (2)

Erik Kaplun
Erik Kaplun

Reputation: 38217

Problem:

You are trying to reference the name name in a lexical context where it's simply not available:

val rule1 = new Tenant(name = "Cooper2",
  (suggestedFloor: Map[String, Int]) => suggestedFloor(name) != groundFloor)

name in this context does not refer to the name defined within Tenant, but instead a to the name name in the scope of the rule1 definition, where of course it obviously does not exist. With this code, the error would disappear but of course this is not what you want:

val name = ??? // this is the `name` that gets referenced by the lambda

val rule1 = new Tenant(name = "Cooper2",
  (suggestedFloor: Map[String, Int]) => suggestedFloor(name) != groundFloor)

Solution:

To work around that, instead of passing in a function at the time of instantiation, use a method override instead:

abstract class Tenant(val name: String) {
  def exclusion(suggestedFloor: Map[String, Int]): Boolean
}

val rule1 = new Tenant(name = "Baker3") {
  def exclusion(suggestedFloor: Map[String, Int]) =
    suggestedFloor(name) != topFloor
}

This creates an anonymous subclass of Tenant with a "custom" definition for exclusion; this, I would say, is also considered idiomatic style in Scala.

Alternatively, you can resort to slightly different semantics and override not a method but an attribute that contains a function instead; this will yield shorter syntax when combined with the more compact form of lambda definition using _:

abstract class Tenant(val name: String) {
  val exclusion: Map[String, Int] => Boolean
}

val rule1 = new Tenant(name = "Baker3") {
  val exclusion = (_: Map[String, Int])(name) != topFloor
}

Unfortunately, the need for the Map[String, Int] re-declaration, is not eliminated by the type inferencer, for reasons only people smarter than me can elaborate on.

Upvotes: 2

4lex1v
4lex1v

Reputation: 21557

You need to curry you function or class declaration:

class Tenant(name: String)(exclusion: Map[String, Int] => Boolean = _(name) != topFloor)

Now make an instance:

scala> new Tenant("hello")()
res8: Tenant = Tenant@13c3c24f

Upvotes: 0

Related Questions