Nico
Nico

Reputation: 391

Scala. Compiler fails when curried function parameter instantiated in different ways

I'm new to Scala, trying to master DSL creation tricks. Can't figure out why compiler behaves differently in the following 2 scenarios when a) I instantiate class B directly and b) when I do the same by wrapper method or value.

object Example {
  def createB: B = new B
  class B

  def createA: A = new A
  class A {
    def aMethod(b: B)(p: String => Unit): Unit = {}
  }

  def main(args: Array[String]) {
    // this compiles fine
    createA aMethod new B { s: String =>
    }

    // this does not compile, as compiler tries to apply function (String => Unit) to class B
    createA aMethod createB { s: String =>
    }

    // this does not compile either
    val bb = new B
    createA aMethod bb { s: String =>
    }
  }
}

Upvotes: 1

Views: 69

Answers (1)

Karl
Karl

Reputation: 1230

Your first example:

createA aMethod new B { s: String => }

may compile, but it isn't doing what you expect. You are actually constructing an instance of B that has the self type of String (refer to this and this for more information). The return type of that statement then is a Function that takes a Function of String to Unit and returns Unit. More simply, you have not provided a value for parameter p to your function, aMethod.

Your issue in all three examples is just a syntactic one. The compiler is really confused and doesn't know where you are giving it a function and where you are extending classes. When that is the case I like to explicitly add the periods and parens for function calls. All three of these should now compile and do what you want:

createA.aMethod(new B) { s: String =>
}

createA.aMethod(createB){ s: String =>
}

val bb = new B
createA.aMethod(bb){ s: String =>
}

Edit: An alternative by directly calling apply on the curried function, this may be your best bet:

class A {
  def aMethod(b: B)(p: String => Unit): Unit = {}
}

def main(args: Array[String]) {
  createA aMethod new B apply { s: String =>
  }

  createA aMethod createB apply { s: String =>
  }

  val bb = new B
  createA aMethod bb apply { s: String =>
  }
}

If you hate using 'apply' you could also construct an object with whatever name you want:

class A {
  def aMethod(b: B) = new {
    def myCoolName(p: String => Unit): Unit = {}
  }
}

def main(args: Array[String]) {
  createA aMethod new B myCoolName { s: String =>
  }

  createA aMethod createB myCoolName { s: String =>
  }

  val bb = new B
  createA aMethod bb myCoolName { s: String =>
  }
}

Another alternative with aMethod not curried:

class A {
  def aMethodNotCurried(b: B, p: String => Unit): Unit = {}
}

def mainNotCurried(args: Array[String]) {
  createA aMethodNotCurried (new B, { s: String =>
  })

  createA aMethodNotCurried (createB, { s: String =>
  })

  val bb = new B
  createA aMethodNotCurried (bb, { s: String =>
  })
}

Upvotes: 1

Related Questions