Shop Learn P1 Docs P2 Docs
Spin2 Operator Syntax - Page 13 — Parallax Forums

Spin2 Operator Syntax

1101113151618

Comments

  • AribaAriba Posts: 2,591
    I wonder how you type x = x+1 into a calculator and what result it shows.

    For sure in mathematics x = x+1 can not be correct. x can not be x and x-1 at the same time. This is only possible if there exists a previous x value, and a next x value and in between happens some procedurale processing. Either by a Spin code or by typing it in a calculator.

    And it's the same with
    5 < x < 10
    in mathematics this shows the possible range of x, but in a programming language it's something different. It's a serie of procedurale steps that execute from left to right.

    Andy
  • AJLAJL Posts: 469
    edited 2017-04-16 07:13
    An alternative here for SPIN2 is to create a pair of ternary instructions and a single constant (NA).
    CLAMP lower value upper
    CLAMPS lower value upper
    

    When the upper or lower limit is not being used it can be replaced with NA. When the byte code compiler encounters the NA it can omit that operation. If only two operands are supplied an error can be thrown to ensure the programmer's intent is captured.

    If no assignment is made the clamped result is placed in value.
  • cgraceycgracey Posts: 13,795
    edited 2017-04-16 07:45
    AJL wrote: »
    An alternative here for SPIN2 is to create a pair of ternary instructions and a single constant (NA).
    CLAMP lower value upper
    CLAMPS lower value upper
    

    When the upper or lower limit is not being used it can be replaced with NA. When the byte code compiler encounters the NA it can omit that operation. If only two operands are supplied an error can be thrown to ensure the programmer's intent is captured.

    If no assignment is made the clamped result is placed in value.

    That would be an interesting use of an operator, having multiple arguments.

    All these efforts are like feeling our way around an invisible shape, without quite the proper tools and approach to conform our thought with syntax, or finally penetrate the shape. Everything we manage to implement is only a half-baked analog to what we mean. I suppose if some nascent key were turned in our minds, we could finally get into the sweet spot where it could all converge absolutely.
  • And maybe @AJL's first post gives a new insight here!

    I think MIN and MAX as functions should be implemented like used in other languages and CLAMP, CLAMPS could solve the whole thing with 3 parameters and a quite nice descriptive name.

    And welcome to the forums AJL!

    Enjoy!

    Mike
  • CLAMP is what I said a while back.
    Avoiding the 3 operand issue, I suggested CLAMPU and CLAMPL to do the clamp as two consecutive operations. Just like Chip has been showing with his example. The more I think about it the more I like this to replace MIN/MAX as they are now.
  • TorTor Posts: 2,010
    jmg wrote: »
    They mean exactly the same thing as before.
    Ah, maybe you have historic hindsight to know they mean the same, but they do not say the same.
    BTW, x := 5 !> x !> 10 does not work. It peaks out at 5 even when x is, say 9, just as x := 5 <# x <# 10 does in Spin1.
    Here you underline the problem.
    You know how the old opcode works, so you are not reading what the symbols say, but are skipping ahead to what you know they will do.

    Sadly, very few new P2 users will know anything about the old opcodes/symbols, so they will read the literal NOT and GREATER.

    x := 5 NOT GREATER than x NOT GREATER than 10 , bounds x within 5..10, and operand order matters.
    The real problem here is that something like x := 5 !> x !> 10 is allowed to work in the first place. It's just too ambiguous for a user to figure out. Certainly if you come from somewhere else. For clamping a value there must be way better options around. Including a 'clamp(x,y)' function-style mechanism, as has been suggested. Crystal clear, unlike the mess above.

  • jmgjmg Posts: 14,967
    edited 2017-04-16 08:39
    AJL wrote: »
    An alternative here for SPIN2 is to create a pair of ternary instructions and a single constant (NA).
    CLAMP lower value upper
    CLAMPS lower value upper
    

    When the upper or lower limit is not being used it can be replaced with NA. When the byte code compiler encounters the NA it can omit that operation. If only two operands are supplied an error can be thrown to ensure the programmer's intent is captured.

    If no assignment is made the clamped result is placed in value.

    Yes, that works too, there was an example back on page 8 ( from code by Jason Dorie) that had the form of
     LowerLimit (operatorX) Value (operatorY) UpperLimit
    
    and that does have a natural order suited to quick glance reading to it.

    an alternative form of
    operator   LowerLimit Value  UpperLimit
    

    also has the same easy to scan benefits, & the benefit of it needs one keyword to set a range, not two.

    Keywords could be CLAMP/CLAMPS/CLAMPU, or RANGE/RANGES/RANGEU or SPAN/SPANS/SPANU


    AJL wrote: »
    An alternative here for SPIN2 is to create a pair of ternary instructions and a single constant (NA).
    Yes, I've seen some languages use NULL, to tell the compiler the programmer deliberately left that empty, and it is commonly used as a 'not initialized' value.

  • Cluso99Cluso99 Posts: 18,063
    edited 2017-04-16 11:09
    What about this...
      #LIMS y <= x <= #               ' lower limit of x (signed) is y (if y > x, replace x)
      #LIMS y <= x <= z               ' limit x (signed) between y and z 
      #LIMS # <= x <= z               ' upper limit of x (signed) is z (if x > z, replace x)
    
    Easy to understand, and # is used when that limit is invalid/not-used.

    Note I used #xxxx for the text operator to avoid using usable/valid labels/variables/constants.
  • Dave HeinDave Hein Posts: 6,347
    edited 2017-04-16 11:55
    This would all be so much easier if we just got rid of the notion of using operators and just replace them with intrinsic function calls, such as min(x,y), max(x,y) and limit(x,y,z). Spin already has intrinsic functions that map directly to byte codes, which are strsize and strcomp. Why not use intrinsic functions to replace <# and #>?

    I would even argue that the ternary operator should be an intrinsic function call, such as lookup(x, y, z) instead of adopting the clumsy C "x?y:z" syntax. At the byte code level the generated code will look the same independent of whether an operator is used or an intrinsic function call is done. Which begs the question of why we are even discussing syntax right now since it doesn't matter at the byte code level. Isn't it more important to get the P2 into synthesis than it is to debate the syntax for <# ?
  • Dave Hein wrote: »
    This would all be so much easier if we just got rid of the notion of using operators and just replace them with intrinsic function calls, such as min(x,y), max(x,y) and limit(x,y,z). Spin already has intrinsic functions that map directly to byte codes, which are strsize and strcomp. Why not use intrinsic functions to replace <# and #>?
    +1

  • Dave Hein wrote: »
    This would all be so much easier if we just got rid of the notion of using operators and just replace them with intrinsic function calls, such as min(x,y), max(x,y) and limit(x,y,z). Spin already has intrinsic functions that map directly to byte codes, which are strsize and strcomp. Why not use intrinsic functions to replace <# and #>?

    I would even argue that the ternary operator should be an intrinsic function call, such as lookup(x, y, z) instead of adopting the clumsy C "x?y:z" syntax. At the byte code level the generated code will look the same independent of whether an operator is used or an intrinsic function call is done. Which begs the question of why we are even discussing syntax right now since it doesn't matter at the byte code level. Isn't it more important to get the P2 into synthesis than it is to debate the syntax for <# ?

    Agreed! I tried to make this argument earlier, but several people balked at it. Well, I did argue the extreme of having no operators at all, which might be pushing things a bit. But I still maintain that we should keep the operator syntax to a minimum. This argument over limit syntax would have been long over if they had just been made into functions to start with.

    A related concern, which I mentioned earlier, is one of namespaces. No, I don't necessarily want to add namespaces. Just that intrinsic functions pollute the global namespace. This wasn't an issue for Spin on P1 because the language was basically static. However, I expect Spin 2 to grow over time. And that means you have a potential for breaking changes if new intrinsic functions are added. One approach to avoiding this is to introduce namespaces, but that brings its own baggage. Adding classes provides a limited namespace, but doesn't address global functions.

    The alternative I was suggesting was to use two different syntaxes for intrinsic function calls and user function calls. That way, the compiler can differentiate names, new intrinsics can be added without fear of name collisions, and the code is self-documenting (you can easily see which functions are user-defined and which are not). As for the actual intrinsic-function syntax, I don't know what that should be.

    Incidentally, in xBase languages, the ternary operator is "iif" (immediate if).
  • Thanks for the welcome Mike.

    I confess that I've been lurking for a while and was essentially proposing a new perspective on other proposals.

    These limit operations seem to be special cases of the ternary conditional assignment operation.
    A #> B
    ' seems equivalent to
    (A>B)?A:B
    
    B <# C
    ' seems equivalent to
    (B<C)?B:C
    

    If intrinsic functions are used I'd suggest select(cond,x,y) rather than lookup(x,y,z), which conjures conflicting memories of Excel formulas, or iif (x,y,z), which doesn't seem education friendly; but limit(minimum, value, maximum) works just as well as clamp(minimum, value, maximum).

    Regards,
    Anthony.
  • Operators are easier to type and easier for the eye to parse. Function calls are okay -- until they become nested. Then it's just a mess of parentheses that you have to unravel to figure out what's really happening. If everything were done with intrinsic function calls, what you would end up with is Forth with parentheses. So I would keep intrinsic function notation to an absolute minimum.

    There's nothing wrong with x := x !< 5 !> 10.

    -Phil
  • kwinnkwinn Posts: 8,697
    Operators are easier to type and easier for the eye to parse. Function calls are okay -- until they become nested. Then it's just a mess of parentheses that you have to unravel to figure out what's really happening. If everything were done with intrinsic function calls, what you would end up with is Forth with parentheses. So I would keep intrinsic function notation to an absolute minimum.

    There's nothing wrong with x := x !< 5 !> 10.

    -Phil

    + 1

    It's as simple as reading a sentence once you have memorized a very small number of operators.
  • Heater.Heater. Posts: 21,230
    Functions are easier to type and easier on the eye to parse. Weird operator symbols are OK, until they become nested. Then it's just a mess of weird symbols that you have to unravel to figure out what's really happening. If everything were done with weird operator symbols, what you would end up with is something indistinguishable from the line noise you get when you have the wrong baud rate setting. As your example shows. So I would keep weird operator symbols to an absolute minimum.

    :) Sorry, could not resist.



  • kwinnkwinn Posts: 8,697
    Heater. wrote: »
    Functions are easier to type and easier on the eye to parse. Weird operator symbols are OK, until they become nested. Then it's just a mess of weird symbols that you have to unravel to figure out what's really happening. If everything were done with weird operator symbols, what you would end up with is something indistinguishable from the line noise you get when you have the wrong baud rate setting. As your example shows. So I would keep weird operator symbols to an absolute minimum.

    :) Sorry, could not resist.

    Can't really argue with that, but nested functions are not much if any better than nested operators in that respect. In both cases it's up to the programmer to make the code as easy to read as possible.
    Operators generally take up less screen space than functions so they can make code easier to read by having a larger portion of code on screen at one time, but I agree that minimizing the number of operators is a good idea.
  • Dave HeinDave Hein Posts: 6,347
    edited 2017-04-16 15:26
    There's nothing wrong with x := x !< 5 !> 10.
    Ask someone who hasn't followed this thread what "x := x !< 5 !> 10" means and I doubt that most people will understand it. Ask any programmer what limit(x, 5, 10) means, and they'll most likely give you the right answer. They'll also probably tell you that max(x,y) produces the maximum value of x and y, and min(x,y) produces the minimum value. The meaning of "x := x !< 5 !> 10" just isn't obvious.

  • Dave Hein wrote: »
    There's nothing wrong with x := x !< 5 !> 10.
    Ask someone who hasn't followed this thread what "x := x !< 5 !> 10" means and I doubt that most people will understand it. Ask any programmer what limit(x, 5, 10) means, and they'll most likely give you the right answer. They'll also probably tell you that max(x,y) produces the maximum value of x and y, and min(x,y) produces the minimum value. The meaning of "x := x !< 5 !> 10" just isn't obvious.
    +1
  • ctwardellctwardell Posts: 1,714
    edited 2017-04-16 15:55
    Small actual project example of how quickly functional only gets ugly.

    I've been doing some work that requires creating encapsulated postscript code. I wrote a quick postscript snippit processor in C# to help debug math operations. The first version created symbolic results using functional notation.

    Here is a postscript function to compute acos(x)

    /acos {dup dup mul 1 exch sub sqrt exch atan} def

    Here is sample input to my snippit processor and resulting output:

    Input
    'x' dup dup mul 1 exch sub sqrt exch atan

    Output
    atan(sqrt(sub(1,mul(x,x))),x)

    The output with functional notation is still not very clear. I made a modified version that uses infix notation for add, sub, mul, div.

    Input
    'x' dup dup mul 1 exch sub sqrt exch atan

    Output
    atan(sqrt(1-x*x),x)

    That result is much easier for me to see what is being calculated.

    I would like to see operations that commonly have infix notation stay with having infix notation in SPIN, operations that are not CRYSTAL CLEAR however I would like to see use functional notation.

    I consider the !< and >! operations to be an example where I personally would prefer functional notation.

    C.W.
  • PUB clamp(x, lo, hi)
    
      return x !< lo !> hi
    

    Problem solved. Infix operators can be used to define functions, but not the other way around, unless the language is extensible. So if you want a function, just write one.

    One problem with intrinsic functions having two or more parameters is that you have to know in which order they occur, if the order matters. And the use of such a function in one's program does not provide any clues. If you write it yourself, it's there in your code as self-documentation.

    -Phil

  • I thought Chip already said Spin will use the !< and !> operators, and what we are helping him decide is what to call the PASM instructions.

    I still think CLAMPU/CLAMPL are good, perhaps CLAMPLO/CLAMPHI are a bit better? Still under 8 characters, so it fits Chip's desire for fitting in a tab stop.

  • ctwardell wrote: »
    Small actual project example of how quickly functional only gets ugly.

    I've been doing some work that requires creating encapsulated postscript code. I wrote a quick postscript snippit processor in C# to help debug math operations. The first version created symbolic results using functional notation.

    Here is a postscript function to compute acos(x)

    /acos {dup dup mul 1 exch sub sqrt exch atan} def

    Here is sample input to my snippit processor and resulting output:

    Input
    'x' dup dup mul 1 exch sub sqrt exch atan

    Output
    atan(sqrt(sub(1,mul(x,x))),x)

    The output with functional notation is still not very clear. I made a modified version that uses infix notation for add, sub, mul, div.

    Input
    'x' dup dup mul 1 exch sub sqrt exch atan

    Output
    atan(sqrt(1-x*x),x)

    That result is much easier for me to see what is being calculated.

    I would like to see operations that commonly have infix notation stay with having infix notation in SPIN, operations that are not CRYSTAL CLEAR however I would like to see use functional notation.

    I consider the !< and >! operations to be an example where I personally would prefer functional notation.

    C.W.
    I don't think anyone is seriously suggesting that everything be moved to functions. The operators that are common across almost all languages can remain operators.

  • cgraceycgracey Posts: 13,795
    These clamping operators are trivial, but their implementation as operators, instead of as functions, offends people because of their past experience, always seeing these presented as functions. I think, anyway.
  • What we got is fine. :D

  • @cgracey,

    At this point, write it the way you want it. This isn't the same as getting input on the P2 instruction set. If someone doesn't like it, they can design their own interpreter!
  • cgracey wrote:
    These clamping operators are trivial, but their implementation as operators, instead of as functions, offends people because of their past experience, always seeing these presented as functions. I think, anyway.
    I'm sure that's true. But I think it's important to distinguish clamping operations from the traditional MIN and MAX functions that people are used to. I still think it's possible to make a reasonable stab at an operator syntax for clamping (in addition to the single ops, !< and !>). How about this:
    x := 0 -[ x ]- 10
    

    The -/b] and [b- pair form a trinary operator, and they look like clamps. Yes? No?

    -Phil
  • cgraceycgracey Posts: 13,795
    cgracey wrote:
    These clamping operators are trivial, but their implementation as operators, instead of as functions, offends people because of their past experience, always seeing these presented as functions. I think, anyway.
    I'm sure that's true. But I think it's important to distinguish clamping operations from the traditional MIN and MAX functions that people are used to. I still think it's possible to make a reasonable stab at an operator syntax for clamping (in addition to the single ops, !< and !>). How about this:
    x := 0 -[ x ]- 10
    

    The -/b] and [b- pair form a trinary operator, and they look like clamps. Yes? No?

    -Phil

    That's the clampiest idea, yet.
  • Isn't that more or less the same as "0 !< x !> 10" ? Or what if you did "0 <# x #> 10"? Oh, isn't that already what we have?
  • AribaAriba Posts: 2,591
    But is this more readable?
      x := max(0,min(x,10))
    
    The usual min/max functions are reversed if you want to limit in a range.
    They are okay to find the min or max, but not if you want to clip to min, max.
    It's the opposite meaning, that's why you never find one set of operators, or function names for both cases.

    Andy
  • jmgjmg Posts: 14,967
    edited 2017-04-17 03:21
    How about this:
    x := 0 -[ x ]- 10
    

    The -/b] and [b- pair form a trinary operator, and they look like clamps. Yes? No?
    Very close, but to me this minor tweak looks more like clamps, very similar to the classic Min/typ/Max of many data sheets
    x := 0 [- x -] 10
    

    a benefit of this choice of glyph pairing, is it has no equivalent clear-but-wrong statement (unlike NOT, GREATER)

    Above would be fine, with the aliases discussed before of
    x := 0 FGE x FLE 10
    
    And, back to REM vs. MOD, in Spin1 the // operator is a remainder; it's not the modulus. If MOD in Spin2 behaves the same way, we need a different mnemonic for it. Anyway, I don't know why we can't just keep //. But if we wanted to, we could add MOD to the mix, which treats the number as unsigned when doing the division.
    I think // may have become the widespread comment to end of line?
    The point about REM & MOD is valid, & many other languages confuse them somewhat, so support for both REM and MOD is fine with me.
    In current PASM, how would the code size compare ?
Sign In or Register to comment.