0

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?

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
Epicurist
  • 903
  • 11
  • 17

2 Answers2

2

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.

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
0

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
4lex1v
  • 21,367
  • 6
  • 52
  • 86
  • Thank for the rapid response, but every instance has another predicate. Its about the actual function literal. – Epicurist Jun 26 '14 at 08:59
  • @Epicurist i'm not sure that i understood what you need. You can pass any function at the declaration or instantiation site. If you want to use your name parameter in your predicate, then you need to curry your class declaration – 4lex1v Jun 26 '14 at 09:04
  • @Epicurist I see, but no, you can't refer param fields that way. But you can pass upper limit in the first arg list and use it in the default function from the second list. – 4lex1v Jun 26 '14 at 09:37