I think it is important to distinguish between a variable that can be read from the outside and one that can be both read and written from the outside. The lack of this distinction is one reason why C++ and Java programmers often use getter/setter methods instead of public instance variables. (The other reason is that a setter method can do more than just set the variable (usually to notify some other code about the changed variable))
Ruby has an interesting approach: one can never access an object's variables from the outside, but when you define a getter method and a setter method whose name ends in an "=", it can be used like a variable. That obviously looses the speed of directly writing to a variable, unless some optimization to detect trivial getters/setters is performed.
Here's an example of how it works for clarification:
class Foo
def myvar()
puts("Getter called!")
return @variable
end
def myvar=(new_value)
puts("Setter called!")
@variable = new_value
end
end
x = Foo.new
x.myvar = 1 # prints Setter called!
x.myvar += 2 # prints Getter called!, then Setter called!
puts(x.myvar)# prints Getter called!, then 3
I think a firewall of methods is a good way to protect an object's internal variables.
What Ruby does there is maybe mainly to simplify source code, so that rather than have a method to set myvar, like 'set_myvar(value)', it lets the programmer write simple assignments which are easier to look at and understand at a glance: 'myvar = 0'. It has the same effect as a method, but it's a lot easier to look at.
What Ruby does there is maybe mainly to simplify source code, so that rather than have a method to set myvar, like 'set_myvar(value)', it lets the programmer write simple assignments which are easier to look at and understand at a glance: 'myvar = 0'. It has the same effect as a method, but it's a lot easier to look at.
Exactly. (It also avoids nonsense like "obj.set_myvar(obj.get_myvar()+2)")
We will end up with chips and no software. This is gating further software development, for me at least. Do we want to wait another ten years?
At the risk of sounding like a broken record, you can program in Spin for the P2 today, and have been able to for some time. Lots of people have had success with FlexGUI/fastspin. If you stick with the basic Spin1 subset (with P2 assembly code in DAT) then I suspect you'll be able to port it over very quickly to Chip's Spin2 when that's available. I think the main benefit of Chip's Spin2 will be the smaller code size and (when it's available) self-hosted compilation, but for now you can certainly get a lot done with fastspin.
[I think the main benefit of Chip's Spin2 will be the smaller code size and (when it's available) native compilation, but for now you can certainly get a lot done with fastspin.
Native compilation? Do you mean a self-hosted development environment? Surely, FastSpin already compiles to native code.
We will end up with chips and no software. This is gating further software development, for me at least. Do we want to wait another ten years?
Actually, it was DONE yesterday, but there has been one more thing I've wanted to add for the first release that I'm implementing today. Just a wafer thin mint. I still need to write the documentation, anyway.
I'm putting a stealth method pointer into the call-stack frame which can be used to vector output anywhere, without the need for passing discrete method pointers around. Methods can set it to whatever they want, as they go, and any methods called inherit the current pointer. It's going to be dreamy.
I'm putting a stealth method pointer into the call-stack frame which can be used to vector output anywhere, without the need for passing discrete method pointers around. Methods can set it to whatever they want, as they go, and any methods called inherit the current pointer. It's going to be dreamy.
Could you describe exactly how this works (from a programmer's perspective, not from the compiler's)? The general idea seems promising, but it also seems like there are a lot of ways that it could go wrong and cause a beginner to get hopelessly tangled up...
[I think the main benefit of Chip's Spin2 will be the smaller code size and (when it's available) native compilation, but for now you can certainly get a lot done with fastspin.
Native compilation? Do you mean a self-hosted development environment? Surely, FastSpin already compiles to native code.
Yes, I did mean self-hosted, thanks. I've corrected my original post.
PUB doit
send := @serial.tx
send("This is a test... ", hex(x), 13)
PRI hex(num)
repeat 8
send(lookup((num ROL= 4) & $F: "0".."9", "A".."F"))
SEND is a method pointer located at long[dbase][-2]. It can be assigned and read. If assigned zero, which is the initial state on cog start, SEND() doesn't do anything.
SEND() is a special method that the compiler generates which has hidden sub-methods for handling strings of bytes, single values, and multiple return values from methods.
I haven't been closely following the Spin2 development, so excuse me for the following dumb question. Will Spin2 methods be able to handle a variable number of parameters? And how does it distinguish between long parameters and string pointers?
I haven't been closely following the Spin2 development, so excuse me for the following dumb question. Will Spin2 methods be able to handle a variable number of parameters? And how does it distinguish between long parameters and string pointers?
At this time, Spin2 methods have fixed numbers of parameters. That could change in the future. For your second question, I'm not sure what you mean.
Chip, in your example you show two different calls to send:
send("This is a test... ", hex(x), 13)
send(lookup((num ROL= 4) & $F: "0".."9", "A".."F"))
In the first case there appear to be 3 parameters, which are a string, the return value of hex() and 13. In the second case there is one parameter, which is the value returned by lookup. Maybe the first call is actually only one parameter with the values of hex(x) and 13 included in the string. However, in the first call you are passing a string pointer, and the second call passes a long value. How does send, or tx know the difference?
The compiler builds the SEND() instance with three different ingredients. It's much different than a normal method call. For every element, the method pointed to by SEND is called with the element as a single parameter, effectively like 'serial.tx(element)'.
1) "This is a test..." gets recognized as a byte string and some bytecodes in the interpreter execute to send all those bytes to the SEND method.
2) 'hex(x)' gets called and then, afterwards, any return values it generated get sent the SEND method. Of course, hex() may do its own SEND() operations.
3) '13' gets sent as a single value to the SEND method.
I think these three cases cover everything, although a z-string output could be added. It might need a keyword.
I think that it would be useful to have a shorter notation for a string pointer, something like:
ser.write(@"hello, world")
instead of
ser.write(string("hello, world"))
I realize that for this particular example you could use the new "send" method instead, but in general strings can be awkward to use in Spin and having an easier way to get pointers to them would be nice.
Chip, thanks for the clarification. To me it seems that if you can do send("abc", hex(5), 13) you should also be able to do serial.tx("abc", hex(5), 13). Of course, this would mean that you don't need the hidden send method pointer. You should just be able to use any method pointer or direct call.
I think that it would be useful to have a shorter notation for a string pointer, something like:
ser.write(@"hello, world")
instead of
ser.write(string("hello, world"))
I realize that for this particular example you could use the new "send" method instead, but in general strings can be awkward to use in Spin and having an easier way to get pointers to them would be nice.
Eric, I like that notation. It's easy to parse, too, because it's just @constant(s). That would be good to replace string() with.
Chip, thanks for the clarification. To me it seems that if you can do send("abc", hex(5), 13) you should also be able to do serial.tx("abc", hex(5), 13). Of course, this would mean that you don't need the hidden send method pointer. You should just be able to use any method pointer or direct call.
Wouldn't we still need the hidden pointer to give hex() an output method?
Public / private syntax gets annoying. If it's not explicitly declared public or shared, it's private.
KISS
I agree.
CON shared i = 2, j = 5, k = 11
m = 1, n = 2
VAR shared x,y,x
a,b,c
We already have public / private syntax (DAT vs VAR) so if DAT variables were accessible by name we wouldn't need to worry about declaring "shared". CON sections are always accessible via obj#constant syntax but variables are not unless explicitly declaring setter/getters. If you wanted to trigger some OTHER more complex behavior, you could always do it the way we have BUT... The idea of being able to access "variables" (even if the limitation is they must be declared in DAT) is very promising. I wonder if @Rayman thinks this would be a reasonable compromise? I find myself creating a lot of setter/getters and I think the declaration of private vs public is inherent..
So, the CONs are all shared already with # to get them...
There would have to be some way to make var public or private...
We have PUB and PRI. Maybe we need VAR and something else for public variable section...
maybe EXT for "external"?
Things in VAR section would be private, things in EXT would be public...
Was just thinking that it would be nice if you could change a "VAR" in a sub-object from a higher level object...
For example, I'm playing around with a VGA driver and would be nice if could do:
vga.boxcolor := $10
To set the VAR section defined variable, "boxcolor", to the value $10
Instead, I currently need to create a function like "SetBoxColor" in the VGA driver as a work around....
+1 for this
... and I would also add two events, if possible. To maintain the same example with the VGA driver, it will be nice that if the compiler founds two subs (in the VGA driver object):
Comments
Yes, I can see that. We really only NEED methods.
Ruby has an interesting approach: one can never access an object's variables from the outside, but when you define a getter method and a setter method whose name ends in an "=", it can be used like a variable. That obviously looses the speed of directly writing to a variable, unless some optimization to detect trivial getters/setters is performed.
Here's an example of how it works for clarification:
No need to change 30 years of semantics at this point...
What Ruby does there is maybe mainly to simplify source code, so that rather than have a method to set myvar, like 'set_myvar(value)', it lets the programmer write simple assignments which are easier to look at and understand at a glance: 'myvar = 0'. It has the same effect as a method, but it's a lot easier to look at.
We will end up with chips and no software. This is gating further software development, for me at least. Do we want to wait another ten years?
At the risk of sounding like a broken record, you can program in Spin for the P2 today, and have been able to for some time. Lots of people have had success with FlexGUI/fastspin. If you stick with the basic Spin1 subset (with P2 assembly code in DAT) then I suspect you'll be able to port it over very quickly to Chip's Spin2 when that's available. I think the main benefit of Chip's Spin2 will be the smaller code size and (when it's available) self-hosted compilation, but for now you can certainly get a lot done with fastspin.
Actually, it was DONE yesterday, but there has been one more thing I've wanted to add for the first release that I'm implementing today. Just a wafer thin mint. I still need to write the documentation, anyway.
I'm putting a stealth method pointer into the call-stack frame which can be used to vector output anywhere, without the need for passing discrete method pointers around. Methods can set it to whatever they want, as they go, and any methods called inherit the current pointer. It's going to be dreamy.
Could you describe exactly how this works (from a programmer's perspective, not from the compiler's)? The general idea seems promising, but it also seems like there are a lot of ways that it could go wrong and cause a beginner to get hopelessly tangled up...
Yes, I did mean self-hosted, thanks. I've corrected my original post.
SEND is a method pointer located at long[dbase][-2]. It can be assigned and read. If assigned zero, which is the initial state on cog start, SEND() doesn't do anything.
SEND() is a special method that the compiler generates which has hidden sub-methods for handling strings of bytes, single values, and multiple return values from methods.
This mechanism can be used to vector data streams anywhere.
At this time, Spin2 methods have fixed numbers of parameters. That could change in the future. For your second question, I'm not sure what you mean.
1) "This is a test..." gets recognized as a byte string and some bytecodes in the interpreter execute to send all those bytes to the SEND method.
2) 'hex(x)' gets called and then, afterwards, any return values it generated get sent the SEND method. Of course, hex() may do its own SEND() operations.
3) '13' gets sent as a single value to the SEND method.
I think these three cases cover everything, although a z-string output could be added. It might need a keyword.
I realize that for this particular example you could use the new "send" method instead, but in general strings can be awkward to use in Spin and having an easier way to get pointers to them would be nice.
Eric, I like that notation. It's easy to parse, too, because it's just @constant(s). That would be good to replace string() with.
Wouldn't we still need the hidden pointer to give hex() an output method?
I'm not sure what's better though... Could it be $ or & or maybe > ?
*?
We already have public / private syntax (DAT vs VAR) so if DAT variables were accessible by name we wouldn't need to worry about declaring "shared". CON sections are always accessible via obj#constant syntax but variables are not unless explicitly declaring setter/getters. If you wanted to trigger some OTHER more complex behavior, you could always do it the way we have BUT... The idea of being able to access "variables" (even if the limitation is they must be declared in DAT) is very promising. I wonder if @Rayman thinks this would be a reasonable compromise? I find myself creating a lot of setter/getters and I think the declaration of private vs public is inherent..
Just my 2c.
I've just tested Pnut v33p for the LOC bug and found it is still present. Further reading - https://forums.parallax.com/discussion/comment/1457051/#Comment_1457051
Here's my latest testing source repacked to suit Pnut. And also the correct output of Fastspin along with the buggy output of Pnut.
Fastspin
Pnut
There would have to be some way to make var public or private...
We have PUB and PRI. Maybe we need VAR and something else for public variable section...
maybe EXT for "external"?
Things in VAR section would be private, things in EXT would be public...
+1 for this
... and I would also add two events, if possible. To maintain the same example with the VGA driver, it will be nice that if the compiler founds two subs (in the VGA driver object): it execute it before an external read access
and executes this after an external write access