For completeness, it should be said that your "apply" can take multiple values, and that "update" works as the dual of "apply", allowing "parentheses overloading" on the left-hand-side of assignments
Class PairMap[A, B, C]{
val contents: mutable.Map[(A,B), C] = new mutable.Map[(A, B), C]();
def apply(a:A, b:B):C = contents.get((a, b))
def update(a:A, b:B, c:C):Unit = contents.put((a, b), c)
}
val foo = new PairMap[String, Int, Int]()
foo("bar", 42) = 6
println(foo("bar", 42)) // prints 6
The primary value of all this is that it keeps people from suggesting extra syntax for things that had to be special-cased in earlier C-family languages (e.g. array element assignment and fetch). It's also handy for factory methods on companion objects. Other than that, care should be taken, as it's one of those things that can easily make your code too compact to actually be readable.