Hello,
I'm trying to create range types with redefined operators but encountering some issues.
My program writes characters to a VGA buffer that has 80 columns and 25 lines. I figured I'd write range types for the columns and lines which I named VGACursor and VGALine respectively, and redefine the += operator so that they wrap around (go back to their low value when superior to their high value).
Here is the code in question:
type
VGABuffer* = ptr array[0..2000, uint16]
VGALine* = distinct range[0..24]
VGACursor* = distinct range[0..79]
FILE = object
var
line: VGALine = 0.VGALine
cursor: VGACursor = 0.VGACursor
proc `+=` (a: VGALine, b: int): VGALine = return ((a.int+b) mod 80).VGALine
proc `+=` (a: VGACursor, b: int): VGACursor =
if (a.int+b) > 25:
line += 1
return ((a.int+b) mod 25).VGACursor
var stdout* {.exportc.} : ptr FILE
var stderr* {.exportc.} : ptr FILE
const video_memory = cast[VGABuffer](0xB8000)
proc flockfile(f: ptr FILE) {.exportc.} = discard
proc funlockfile(f: ptr FILE) {.exportc.} = discard
proc fflush(stream: ptr FILE): cint {.exportc.} = 0.cint
proc fwrite(data: cstring, size: csize_t, nitems: csize_t, stream: ptr FILE): csize_t {.exportc.} =
let
backColour: byte = 0b00000000
foreColour: byte = 0b00001111
colour: byte = (backColour shl 4) or foreColour
for i in 0..<size:
var
c = data[i]
info = c.uint8 or (colour.uint16 shl 8)
if c == '\n':
line += 1
else:
cursor += 1
video_memory[cursor.int+(line.int*80)] = info
line += 1
return nitems
And I get the error: stdio.nim(16, 10) Error: expression 'line += 1' is of type 'VGALine' and has to be used (or discarded)
I don't know what that means, or why it appears since I've declared unused variables with no issues in the past.
Could you help me understand? Also could you tell me if this is the idiomatic solution for creating wrapping range types?
You made a small mistake in your first += proc.
What you want to do in a proc such as that is mutate the existing input, not produce an output.
You want to change the existing VGALine in a (and by extension: line) instead of making an entirely new one, right? You did the opposite and created a new one (so line += 1 creates a new VGALine value).
And since nim doesn't allow you to use a proc that returns something without explicitly saying that you don't want it to return something, this is the compiler basically refusing to compile something that it is betting on is an accident.
Try to replace you += proc for VGALine with something like this:
proc `+=` (a: var VGALine, b: int) =
a = ((a.int+b) mod 80).VGALine
Note the error is basically something you'd encounter here as well:
proc x(): int =
echo "Something"
return 5
x()
This will not compile because nim will throw the same error as you got. The reasoning is:
If you call a proc that returns an output, the vast majority 99% of the time you actually want the value of that output. Ignoring that output is typically a user-error. For the cases where it isn't, you have discard so you can explicitly say "I only want to execute this proc, I don't care for the output".
So to make the example above work, you'd need to explicitly discard the return from x():
proc x(): int =
echo "Something"
return 5
discard x()
Yeah but that's the mistake, you don't want it to return anything.
+= is an operator that merges the right-hand value into the left-hand value. There is no third value being created, just 2 values being merged. Which means instead of returning, your first value should be mutable so you can modify it to merge the second value into it.
By defining it as "proc +=`(): SomeType" you have it instead create a third value that gets returned. It might make sense to regard `x += y as basically the same kind of expression as a.myProc(b). If that returns something you got to discard it, if it doesn't you don't.
It does, although it also does the operation in place. I checked before writing it to be sure I wasn't misremembering.
From the docs: These methods are called to implement the augmented arithmetic assignments (+=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=). These methods should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self)
How have you checked?
❯ py
>>> a = 0
>>> b = (a += 1)
File "<stdin>", line 1
b = (a += 1)
^^
SyntaxError: invalid syntax
I don't know what that means, or why it appears since I've declared unused variables with no issues in the past.
It means that you have to either assign it to a variable or to discard it explicitly or to mark the proc as {.discardable.}. However, your current implementation doesn't actually modify any of its argument, and thus it becomes a no-op if you discard the result. The error message was trying to tell you that you might be losing important information by discarding it.
As for
I've declared unused variables with no issues in the past.
It's because declaring an unused variable isn't disallowed by the language specification, while discarding the result (if any) of a procedure call is.