7

Just started programming in the Nim language (which I really like so far). As a learning exercise I am writing a small matrix library. I have a bunch more code, but I'll just show the part that's relevant to this question.

type
  Matrix*[T; nrows, ncols: static[int]] = array[0 .. (nrows * ncols - 1), T]           

# Get the index in the flattened array corresponding 
# to row r and column c in the matrix
proc index(mat: Matrix, r, c: int): int =
  result = r * mat.ncols + c

# Return the element at r, c
proc `[]`(mat: Matrix, r, c: int): Matrix.T =
  result = mat[mat.index(r, c)]

# Set the element at r, c
proc `[]=`(mat: var Matrix, r, c: int, val: Matrix.T) =  
  mat[mat.index(r, c)] = val

# Add a value to every element in the matrix
proc `+=`(mat: var Matrix, val: Matrix.T) =
  for i in 0 .. mat.high:
     mat[i] += val         

# Add a value to element at r, c
proc `[]+=`(mat: var Matrix, r, c: int, val: Matrix.T) =  
  mat[mat.index(r, c)] += val


# A test case
var mat: Matrix[float, 3, 4] # matrix with 3 rows and 4 columns
mat[1, 3] = 7.0
mat += 1.0

 # add 8.0 to entry 1, 3 in matrix
`[]+=`(mat, 1, 3, 8.0) # works fine

All this works fine, but I'd like to be able to replace the last line with something like

mat[1, 3] += 4.0

This won't work (wasn't expecting it to either). If I try it, I get

Error: for a 'var' type a variable needs to be passed

How would I create an addition assignment operator that has this behavior? I'm guessing I need something other than a proc to accomplish this.

Grzegorz Adam Hankiewicz
  • 7,349
  • 1
  • 36
  • 78
COM
  • 847
  • 9
  • 23
  • 3
    Funny, I did exactly the same yesterday, and had the same problem. What about changing the title to _Nim operator overloading: How to combine `[]` and `+=`?_ or something like that to make the actual topic more clear? – bluenote10 Apr 07 '15 at 10:55

1 Answers1

7

There are two ways you can do this:

  • Overload [] for var Matrix and return a var T (This requires the current devel branch of Nim):

    proc `[]`(mat: Matrix, r, c: int): Matrix.T =
      result = mat[mat.index(r, c)]
    
    proc `[]`(mat: var Matrix, r, c: int): var Matrix.T =
      result = mat[mat.index(r, c)]
    
  • Make [] a template instead:

    template `[]`(mat: Matrix, r, c: int): expr =
      mat[mat.index(r, c)]
    

    This causes a problem when mat is not a value, but something more complex:

    proc x: Matrix[float, 2, 2] =
      echo "x()"
    
    var y = x()[1, 0]
    

    This prints x() twice.

def-
  • 5,275
  • 20
  • 18
  • Went with the second approach. Works nicely. Thanks. – COM Apr 05 '15 at 20:44
  • Also tried the first approach. Gave the error `Error: ambiguous call`. I'm assuming this is because I'm not using the latest devel branch (using stable version 0.10.2). Just want to make sure it's not some other issue. – COM Apr 07 '15 at 17:10