Reputation: 12356
I'm trying to have curried apply and update methods like this:
def apply(i: Int)(j: Int) = matrix(i)(j)
def update(i: Int, j: Int, value: Int) =
new Matrix(n, m, (x, y) => if ((i,j) == (x,y)) value else matrix(x)(y))
Apply method works correctly, but update method complains:
scala> matrix(2)(1) = 1
<console>:16: error: missing arguments for method apply in class Matrix;
follow this method with `_' if you want to treat it as a partially applied function
matrix(2)(1) = 1
Calling directly update(2)(1)(1)
works, so it is a conversion to update method that doesn't work properly. Where is my mistake?
Upvotes: 2
Views: 881
Reputation: 23046
The desugaring of assignment syntax into invocations of update
maps the concatenation of a single argument list on the LHS of the assignment with the value on the RHS of the assignment to the first parameter block of the update
method definition, irrespective of how many other parameter blocks the update
method definition has. Whilst this transformation in a sense splits a single parameter block into two (one on the LHS, one on the RHS of the assignment), it will not further split the left parameter block in the way that you want.
I also think you're mistaken about the example of the explicit invocation of update
that you show. This doesn't compile with the definition of update
that you've given,
scala> class Matrix { def update(i: Int, j: Int, value: Int) = (i, j, value) }
defined class Matrix
scala> val m = new Matrix
m: Matrix = Matrix@37176bc4
scala> m.update(1)(2)(3)
<console>:10: error: not enough arguments for method update: (i: Int, j: Int, value: Int)(Int, Int, Int).
Unspecified value parameters j, value.
m.update(1)(2)(3)
^
I suspect that during your experimentation you actually defined update like so,
scala> class Matrix { def update(i: Int)(j: Int)(value: Int) = (i, j, value) }
defined class Matrix
The update desugaring does apply to this definition, but probably not in the way that you expect: as described above, it only applies to the first argument list, which leads to constructs like,
scala> val m = new Matrix
m: Matrix = Matrix@39741f43
scala> (m() = 1)(2)(3)
res0: (Int, Int, Int) = (1,2,3)
Here the initial one-place parameter block is split to an empty parameter block on the LHS of the assignment (ie. the ()
) and a one argument parameter block on the RHS (ie. the 1
). The remainder of the parameter blocks from the original definition then follow.
If you're surprised by this behaviour you won't be the first.
The syntax you're after is achievable via a slightly different route,
scala> class Matrix {
| class MatrixAux(i : Int) {
| def apply(j : Int) = 23
| def update(j: Int, value: Int) = (i, j, value)
| }
|
| def apply(i: Int) = new MatrixAux(i)
| }
defined class Matrix
scala> val m = new Matrix
m: Matrix = Matrix@3af30087
scala> m(1)(2) // invokes MatrixAux.apply
res0: Int = 23
scala> m(1)(2) = 3 // invokes MatrixAux.update
res1: (Int, Int, Int) = (1,2,3)
Upvotes: 7
Reputation: 12852
My guess is, that it is simply not supported. Probably not due to an explicit design decision, because I don't see why it shouldn't work in principle.
The translation concerned with apply
, i.e., the one performed when converting m(i)(j)
into m.apply(i, j)
seems to be able to cope with currying. Run scala -print
on your program to see the code resulting from the translation.
The translation concerned with update
, on the other hand, doesn't seem to be able to cope with currying. Since the error message is missing arguments for method apply
, it even looks as if the currying confuses the translator such that it tries to translate m(i)(j) = v
into m.apply
, but then screws up the number of required arguments. scala -print
unfortunately won't help here, because the type checker terminates the translation too early.
Here is what the language specs (Scala 2.9, "6.15 Assignments") say about assignments. Since currying is not mentioned, I assume that it is not explicitly supported. I couldn't find the corresponding paragraph for apply
, but I guess it is purely coincidental that currying works there.
An assignment f(args) = e with a function application to the left of the ‘=’ operator is interpreted as f.update(args, e), i.e. the invocation of an update function defined by f.
Upvotes: 0