Post Reply 
newRPL - Updated to build 1497 [official build remains at 1487]
09-07-2021, 08:54 PM
Post: #181
RE: newRPL - Updated to build 1497 [official build remains at 1487]
(09-07-2021 06:07 PM)JoJo1973 Wrote:  What do you think?

An alternative approach to parameter checking that results in a very compact check at the start of a program was written for the HP50G and appears in Datafile V27N4P20 (available on Jake Schwartz's PPC Computer Archive collection).

For example a program starting << "Xnu" PTYPE ... >> will abort with an error message (the 'X') unless the stack contains a number (real or integer) on level 1 and a number with units attached in level 2.

I'd be happy for Claudio to include something like PTYPE in newRPL.
Find all posts by this user
Quote this message in a reply
09-08-2021, 03:10 PM (This post was last modified: 09-08-2021 03:14 PM by Claudio L..)
Post: #182
RE: newRPL - Updated to build 1497 [official build remains at 1487]
(09-07-2021 06:07 PM)JoJo1973 Wrote:  I'd like to submit my entry for the Suggested Command of the Month© contest... an implementation of the Check&Dispatch structure!

As we all know, all RPL variants encourage a bottom-up approach to programming and this means that usually there is a main program that crunches the input data to ensure it is valid before calling the actual subroutines.

The problem is that this task can become incredibly boring and convoluted, especially if the objective is to write a library. The simple task to check for three arguments on the stack being a list and two positive integers robs the programming of half the fun!

To ease the burden nothing beats the elegance of SystemRPL's Ck&Dispatch structure which I propose to implement in NewRPL as follows:

Code:

CHECK
    { arglist1 } THEN
        ...
    END
    { arglist2 } THEN
        ...
    END
    .
    .
    .
    ELSE
        ...
    END
END
Each arglist is a list of real numbers, where positive ones are compared against the output of TYPE (if integer) / TYPEE (if they have fractional part), negative one against the output of VTYPE/VTYPEE and 0's are just placeholders meaning "any type".

The check mechanism would retain SystemRPL behaviour wrt tagged objects: the arguments are checked twice; in the first pass tagged objects trigger a match only if arglist explicitly requires a tagged object; in the second pass the tags are stripped and the payload is checked to trigger a match.

It's not mandatory for the arglists to have the same length: the first valid match is dispatched.

The ELSE clause is optional: the default behaviour is "Bad Argument Count" in case of total mismatch or "Bad Argument Type" if at least there is a count match.

Some arglist examples:
Code:

{ } THEN 'SUB0' XEQ END                   @ No arguments: trivial but legal

{ 0 0 0 } THEN 'SUB1' XEQ END             @ Three items on the stack, no matter their type

{ 62 10 } THEN 'SUB2' XEQ END             @ A list and a real number (any kind of real: integer, not integer, exact, approximate, binary, hexadecimal...)

{ 52 10.22 10.33 } THEN 'SUB3' XEQ END    @ A matrix and two real numbers, an approx octal and an exact hex

{ -62 0 10.12 } THEN 'SUB4' XEQ END       @ A variable containing a list, a generic object
                                          @ and a real number, integer and exact
Of course the CHECK..END structure isn't necessarily used to call other programs:
Code:

«
  CHECK
    { 62 } THEN
      « CHECK { 10.12 } THEN DROP 1 END ELSE DROP 0 END »
      1
      DOLIST
      ΠLIST
    END
    { -62 } THEN
      DUP RCL
      « CHECK { 10.12 } THEN DROP 1 END ELSE DROP 0 END »
      1
      DOLIST
      ΠLIST
    END
  END
»
Returns 1 if the argument is a list (or a variable storing a list) of undetermined size containing integer, exact elements and 0 otherwise. I'm assuming here that CHECK doesn't 'consume' the stack.

The arglist concept could also be useful during library creation: at the moment the programmer must provide the number of parameters; instead it could provide a "list of arglists" albeit with the limitation that they must have the same size to comply with NewRPL programming practices.

The beauty is also that as long as sub-type identification becomes smarter, CHECK..END becomes more powerful.

What do you think?

Looks nice and clean! But maybe we can reuse the CASE structure with some clever commands and not introduce another complex flow control structure into the language.
On another hand, the CK&Dispatch method almost never worked for me in real life, except for very simple commands.
You end up with situations where 5 or 10 cases are needed for the same basic code, then other 5 or 10 for another, etc.
A very simple example, some code takes 1 argument: can be a real or a complex. Your code works the same whether is a real or complex, all you need is a "numeric". Also you may accept the complex i constant (which has a different type but it's 100% equivalent to (0,1)), so you'll end up with 3 argument list cases for the exact same code. Now let's imagine one that takes 2 of these arguments. Your list cases will need to cover all combinations: real/real, real/complex, real/i, complex/real, complex/complex, complex/i, i/real, i/complex, i/i.
So it explodes into a nightmare really easily. It would be OK if you needed different code for each case, but in most cases you don't.
What I find works better for me is to check the arguments one by one, and each argument against a list of valid types, not just one type.
Something like:
Code:

1 { 10 30 55 } ARGCK
In this case, ARGCK takes a stack level and a list of valid types. It will error Bad Argument Type if there's no match, or continue execution if everything is fine. There could also be a version that doesn't error but returns true/false for example, to be used in IF or CASE statements.
In the example above with 2 arguments, you could simply do them in sequence:
Code:

1 { 10 30 55 } ARGCK
2 { 10 30 55 } ARGCK

If it didn't error, you have the right types. Now let's say some cases need special handling, in the example above we may want to convert the constant 'i' into a complex number by doing ->NUM, and perhaps accept other things like a complex number in symbolic form '1+i'.
Let's say we create the ARGCKTF command (ARG ChecK True/False, I'm open to better name suggestions) that doesn't error, we can use IF to simply do ->NUM and try again:
Code:

IF 1 { 55 32 56 } ARGCKTF THEN ->NUM END
1 {10 30 } ARGCK

So we check first if argument 1 is a constant, a variable identifier (because, why not?) or a symbolic and we run ->NUM. After that, we check that we ended up either with a real or a complex number, and error if not.

This is some hybrid between what Bruce proposed (one compact command that errors if there's no match), your proposal using a list of TYPE/TYPEE numbers and my own spin checking arguments one by one in sequence rather than in bulk.

Can you think of cases where these 2 commands would fail to work well?

**EDIT**: By the way, if the depth of the stack is less than the argument number you passed to ARGCK, then it will error Bad Argument Count so that case is covered and you never need to provide an argument count.
Find all posts by this user
Quote this message in a reply
09-08-2021, 05:21 PM
Post: #183
RE: newRPL - Updated to build 1497 [official build remains at 1487]
I believe that a custom case structure is an overengineered solution, the simple command CKARG appears more reasonable (a building block that can be used in a normal case structure, or in a wholly different manner if you want).

If I may, I'd like to request replacing the simple true/false flag with an index of which type in the list matched (with 0 as placeholder for no match, like POS does). In RPL anything that isn't 0 counts as true anyway, so the value can be used for the same purposes, and performance-wise it should be a freebie, but in situations where the cases are similar but not quite the same the programmer could use this index to pull the type-specific handling code out of a list, then follow up with the common code. For instance, if your program shall accept a list or a vector and treat either as coordinates, that would allow something like this, based around AXL to coerce the input into one canonical format:
Code:
... CKARGTF
IF DUP NOT
THEN DROP "Bad Argument Type" DOERR
ELSE { AXL \<< \>> } SWAP GET EVAL
  ...
END
(NOP would be nice to have by the way, here I used an empty program instead. And yes, I'm using traditional RPL trigraphs instead of newRPL's Unicode glyphs; should be understandable either way.)
This kind of dispatching also opens the door to a third command that returns the index but retains the error-out on no match.

In the end, CKARGTF would arrive at something like this UserRPL implementation:
Code:
\<<
  SWAP
  1. + (adjusting stack level parameter for the types list in the way)
  PICK (fetch the object for examination; also serves as a stack depth guard)
  TYPE POS
\>>
This one doesn't have auto-detagging and such, but it should serve as a prototype to experiment with before deciding on whether to implement it as a native command. Additional features can be prototyped as wrappers around this, e.g. the error-checking CKARG would be
Code:
\<<
  IF CKARGTF NOT
  THEN "Bad Argument Type" DOERR
  END
\>>
As a hint for a tag-stripping wrapper, DTAG would be the key command; though I didn't check if newRPL has that one yet.


Side note: I'm a SysRPL programmer, but in >90% of my programs type-checking doesn't use CK&Dispatch1 or its siblings. It's not as awesome as it appears to be. Checking each stack level (or group of them, in some cases) in turns is where it's at.
Find all posts by this user
Quote this message in a reply
09-08-2021, 05:35 PM
Post: #184
RE: newRPL - Updated to build 1497 [official build remains at 1487]
(09-08-2021 03:10 PM)Claudio L. Wrote:  Looks nice and clean! But maybe we can reuse the CASE structure with some clever commands and not introduce another complex flow control structure into the language.
On another hand, the CK&Dispatch method almost never worked for me in real life, except for very simple commands.
You end up with situations where 5 or 10 cases are needed for the same basic code, then other 5 or 10 for another, etc.
A very simple example, some code takes 1 argument: can be a real or a complex. Your code works the same whether is a real or complex, all you need is a "numeric". Also you may accept the complex i constant (which has a different type but it's 100% equivalent to (0,1)), so you'll end up with 3 argument list cases for the exact same code. Now let's imagine one that takes 2 of these arguments. Your list cases will need to cover all combinations: real/real, real/complex, real/i, complex/real, complex/complex, complex/i, i/real, i/complex, i/i.
So it explodes into a nightmare really easily. It would be OK if you needed different code for each case, but in most cases you don't.

Well, Ck&Dispatch is probably best suited for Saturn architecture: your 'orthogonal' approach yields more synthetic code so I'm all for it.

Quote:What I find works better for me is to check the arguments one by one, and each argument against a list of valid types, not just one type.
Something like:
Code:

1 { 10 30 55 } ARGCK
In this case, ARGCK takes a stack level and a list of valid types. It will error Bad Argument Type if there's no match, or continue execution if everything is fine. There could also be a version that doesn't error but returns true/false for example, to be used in IF or CASE statements.
In the example above with 2 arguments, you could simply do them in sequence:
Code:

1 { 10 30 55 } ARGCK
2 { 10 30 55 } ARGCK

If it didn't error, you have the right types. Now let's say some cases need special handling, in the example above we may want to convert the constant 'i' into a complex number by doing ->NUM, and perhaps accept other things like a complex number in symbolic form '1+i'.
Let's say we create the ARGCKTF command (ARG ChecK True/False, I'm open to better name suggestions) that doesn't error, we can use IF to simply do ->NUM and try again:
Code:

IF 1 { 55 32 56 } ARGCKTF THEN ->NUM END
1 {10 30 } ARGCK

So we check first if argument 1 is a constant, a variable identifier (because, why not?) or a symbolic and we run ->NUM. After that, we check that we ended up either with a real or a complex number, and error if not.

This is some hybrid between what Bruce proposed (one compact command that errors if there's no match), your proposal using a list of TYPE/TYPEE numbers and my own spin checking arguments one by one in sequence rather than in bulk.

It's pretty versatile, what about 'CHECK' for the T/F version and 'CHECKFAIL' for the other one?

What I think is important is to keep the usage of negative types to differentiate between TYPEE/VTYPEE and the automatic stripping of tags à la Ck&Dispatch

BTW CHECK could return the matching type or 0 instead of a plain 1/0.

Quote:Can you think of cases where these 2 commands would fail to work well?

**EDIT**: By the way, if the depth of the stack is less than the argument number you passed to ARGCK, then it will error Bad Argument Count so that case is covered and you never need to provide an argument count.

That's good!
Find all posts by this user
Quote this message in a reply
09-08-2021, 09:28 PM
Post: #185
RE: newRPL - Updated to build 1497 [official build remains at 1487]
(09-08-2021 05:21 PM)3298 Wrote:  If I may, I'd like to request replacing the simple true/false flag with an index of which type in the list matched (with 0 as placeholder for no match, like POS does). In RPL anything that isn't 0 counts as true anyway, so the value can be used for the same purposes, and performance-wise it should be a freebie, but in situations where the cases are similar but not quite the same the programmer could use this index to pull the type-specific handling code out of a list, then follow up with the common code. For instance, if your program shall accept a list or a vector and treat either as coordinates, that would allow something like this, based around AXL to coerce the input into one canonical format:
Code:
... CKARGTF
IF DUP NOT
THEN DROP "Bad Argument Type" DOERR
ELSE { AXL \<< \>> } SWAP GET EVAL
  ...
END

I like it, well thought out and minimal implementation effort on my part.

(09-08-2021 05:21 PM)3298 Wrote:  (NOP would be nice to have by the way, here I used an empty program instead. And yes, I'm using traditional RPL trigraphs instead of newRPL's Unicode glyphs; should be understandable either way.)
This kind of dispatching also opens the door to a third command that returns the index but retains the error-out on no match.

Perhaps there should be only one command that ALWAYS return a result, worst case if you don't need it just DROP it.


(09-08-2021 05:21 PM)3298 Wrote:  Side note: I'm a SysRPL programmer, but in >90% of my programs type-checking doesn't use CK&Dispatch1 or its siblings. It's not as awesome as it appears to be. Checking each stack level (or group of them, in some cases) in turns is where it's at.

Yeah, that was my experience as well, looks great on paper but doesn't help at the most basic things.

(09-08-2021 05:35 PM)JoJo1973 Wrote:  Well, Ck&Dispatch is probably best suited for Saturn architecture: your 'orthogonal' approach yields more synthetic code so I'm all for it.

[quote='JoJo1973' pid='151885' dateline='1631122553']
It's pretty versatile, what about 'CHECK' for the T/F version and 'CHECKFAIL' for the other one?
I used ARGCK, 3298 used CKARG (I think it's better than mine) and you used CHECK. I don't know, we can always leave that decision for the last minute, once we agree on the functionality of each command maybe the right the name will come to us.

(09-08-2021 05:35 PM)JoJo1973 Wrote:  What I think is important is to keep the usage of negative types to differentiate between TYPEE/VTYPEE and the automatic stripping of tags à la Ck&Dispatch

BTW CHECK could return the matching type or 0 instead of a plain 1/0.
I like the idea of the negative numbers, but perhaps we can expand it a little more.
As far as TAGs, all tagged objects TYPE is 10000 + the type of the tagged object, so you don't really need to strip the tag to type check it.

Perhaps we could use exact/approximated numbers in the list to indicate a "loose" comparison.
where:
* First the objects are type-matched exactly.
* Tagged objects are type-matched after subtracting 10000 to the type
If the number in the argument list is exact, comparison ends here.
If there's no match, it continues as follows...
If the number in the list is approximate, additional checks are done:
* Variable names are recalled and their content is type-matched.
* Constants are replaced with their numeric values then type-matched
* Symbolic expressions are evaluated with ->NUM and the result is type-matched
* Programs are executed with XEQ and the result type-matched (that program better leave one result on the stack...)

Some examples:
10. ----> Match a real number, even when inside a variable, or an expression that evaluates to a real number, or a tagged real.
10010 -----> Match a tagged real number only, tag is required.
10010. -----> Match a tagged real inside a variable
10000 -----> Match any tagged object

We still have the negative sign to do other stuff.
Find all posts by this user
Quote this message in a reply
09-08-2021, 10:40 PM
Post: #186
RE: newRPL - Updated to build 1497 [official build remains at 1487]
(09-08-2021 09:28 PM)Claudio L. Wrote:  As far as TAGs, all tagged objects TYPE is 10000 + the type of the tagged object, so you don't really need to strip the tag to type check it.

Huh, didn't realize it!

Quote:Perhaps we could use exact/approximated numbers in the list to indicate a "loose" comparison.
where:
* First the objects are type-matched exactly.
* Tagged objects are type-matched after subtracting 10000 to the type
If the number in the argument list is exact, comparison ends here.
If there's no match, it continues as follows...
If the number in the list is approximate, additional checks are done:
* Variable names are recalled and their content is type-matched.
* Constants are replaced with their numeric values then type-matched
* Symbolic expressions are evaluated with ->NUM and the result is type-matched
* Programs are executed with XEQ and the result type-matched (that program better leave one result on the stack...)

Some examples:
10. ----> Match a real number, even when inside a variable, or an expression that evaluates to a real number, or a tagged real.
10010 -----> Match a tagged real number only, tag is required.
10010. -----> Match a tagged real inside a variable
10000 -----> Match any tagged object

We still have the negative sign to do other stuff.

But this would mean to devise a comparison schema that is radically different from the one used by TYPEE. We would end having two incomplete features instead of one.

Inspired by the initial implementation of TYPEE I drafted a proposal that can be adapted to all object types and it's based on the principle that each property is encoded by a decimal digit different from zero. The zero being a wildcard when the extended type is interpreted in the context of type matching.

For example, the extended type for real numbers would be like this:

   

Therefore the extended type of 123.45. becomes 10.12122 and you can extract (with DIGITS perhaps?) all the info you might need.

In type-checking this value would still match against 10(.00000) if you need a simple real or against 10.1002 if you need a finite approx.

Real numbers are the building block of more complicate types therefore if you need to check for a temperature increment greater or equal to zero (or a variable containing it) you would do

Code:

IF 1 { 54.04 -54.04 } ARGCKTF THEN ->NUM UVAL END
1 { 10.100011 } ARGCK

Since unit subtype describes only properties relevant to unit objects and not relevant to the "payload".


Attached File(s)
.xlsx  NewRPL_Types.xlsx (Size: 11.75 KB / Downloads: 6)
Find all posts by this user
Quote this message in a reply
09-08-2021, 10:50 PM
Post: #187
RE: newRPL - Updated to build 1497 [official build remains at 1487]
(09-08-2021 09:28 PM)Claudio L. Wrote:  I like the idea of the negative numbers, but perhaps we can expand it a little more.
As far as TAGs, all tagged objects TYPE is 10000 + the type of the tagged object, so you don't really need to strip the tag to type check it.

Perhaps we could use exact/approximated numbers in the list to indicate a "loose" comparison.
where:
* First the objects are type-matched exactly.
* Tagged objects are type-matched after subtracting 10000 to the type
If the number in the argument list is exact, comparison ends here.
If there's no match, it continues as follows...
If the number in the list is approximate, additional checks are done:
* Variable names are recalled and their content is type-matched.
* Constants are replaced with their numeric values then type-matched
* Symbolic expressions are evaluated with ->NUM and the result is type-matched
* Programs are executed with XEQ and the result type-matched (that program better leave one result on the stack...)

Some examples:
10. ----> Match a real number, even when inside a variable, or an expression that evaluates to a real number, or a tagged real.
10010 -----> Match a tagged real number only, tag is required.
10010. -----> Match a tagged real inside a variable
10000 -----> Match any tagged object

We still have the negative sign to do other stuff.

Oops, I re-read it less hastily and I realize that your extension is not in conflict with extended types... Now I understand your point!
Find all posts by this user
Quote this message in a reply
09-09-2021, 03:19 AM
Post: #188
RE: newRPL - Updated to build 1497 [official build remains at 1487]
(09-08-2021 10:50 PM)JoJo1973 Wrote:  Oops, I re-read it less hastily and I realize that your extension is not in conflict with extended types... Now I understand your point!

Yes, I never meant to break the extended type checking. Just that in my examples I used only integers. But I like the extend type information as a means to reject arguments out of range. Although the automatic argument check will not produce an error message as useful as if the programmer does it manually. I like newRPL style errors like "Expected an integer number" rather than "Bad Argument Type".
The automatic checker will never give an error like "Expected a real number 0<x<30" unfortunately.
Find all posts by this user
Quote this message in a reply
09-09-2021, 07:49 AM (This post was last modified: 09-09-2021 07:51 AM by JoJo1973.)
Post: #189
RE: newRPL - Updated to build 1497 [official build remains at 1487]
(09-09-2021 03:19 AM)Claudio L. Wrote:  The automatic checker will never give an error like "Expected a real number 0<x<30" unfortunately.

ARGCK could accept a third parameter:

0: Bad type/count error
Integer: Issue corresponding error
String: DOERR
Program/Identifier: Error handler
Find all posts by this user
Quote this message in a reply
09-09-2021, 03:47 PM
Post: #190
RE: newRPL - Updated to build 1497 [official build remains at 1487]
(09-09-2021 07:49 AM)JoJo1973 Wrote:  
(09-09-2021 03:19 AM)Claudio L. Wrote:  The automatic checker will never give an error like "Expected a real number 0<x<30" unfortunately.

ARGCK could accept a third parameter:

0: Bad type/count error
Integer: Issue corresponding error
String: DOERR
Program/Identifier: Error handler

OK, so let's get organized:
The commands would be then 2 separate ones:
Code:

<arg # (stack level)> <List of valid types> CKARG
<arg # (stack level)> <List of valid types> <Error/Message> CKARGERR

Where the <Error/Message> parameter can be as you described above.

CKARG would return the POS of the item that matches, or 0 if no match. No error is thrown.
CKARGERR returns the POS of the item, otherwise throws the given error message (or XEQ the handler).
Now there's 2 possible error conditions:
a) If the stack is not deep enough ---> issues a "Bad Argument Count" error, it does NOT run the handler or use the given message
b) If there's no match ---> Custom message / XEQ error handler

Are we good with this? I think specs are pretty much finished, let's agree on names and I can get coding.
Find all posts by this user
Quote this message in a reply
09-09-2021, 05:25 PM
Post: #191
RE: newRPL - Updated to build 1497 [official build remains at 1487]
(09-09-2021 03:47 PM)Claudio L. Wrote:  
(09-09-2021 07:49 AM)JoJo1973 Wrote:  ARGCK could accept a third parameter:

0: Bad type/count error
Integer: Issue corresponding error
String: DOERR
Program/Identifier: Error handler

OK, so let's get organized:
The commands would be then 2 separate ones:
Code:

<arg # (stack level)> <List of valid types> CKARG
<arg # (stack level)> <List of valid types> <Error/Message> CKARGERR

Where the <Error/Message> parameter can be as you described above.

CKARG would return the POS of the item that matches, or 0 if no match. No error is thrown.
CKARGERR returns the POS of the item, otherwise throws the given error message (or XEQ the handler).
Now there's 2 possible error conditions:
a) If the stack is not deep enough ---> issues a "Bad Argument Count" error, it does NOT run the handler or use the given message
b) If there's no match ---> Custom message / XEQ error handler

Are we good with this? I think specs are pretty much finished, let's agree on names and I can get coding.

Sure, Bad Argument Count should have the precedence.
Find all posts by this user
Quote this message in a reply
10-04-2021, 09:17 PM
Post: #192
RE: newRPL - Updated to build 1497 [official build remains at 1487]
Code:

{ 1 2 { 3 4 { 5 } } « →STR » MAPLIST→
returns
Code:

"1"
"2"
{ "3" "4" { "5" } }
instead of
Code:

"1"
"2"
"3"
"4"
"5"

It seems to me that since the program is mapped to all elements thru full depth then all the sublists should be exploded, but maybe that's a design choice and not a bug.

Claudio could you clarify?
Find all posts by this user
Quote this message in a reply
10-06-2021, 12:32 AM (This post was last modified: 10-06-2021 12:38 AM by Claudio L..)
Post: #193
RE: newRPL - Updated to build 1497 [official build remains at 1487]
(10-04-2021 09:17 PM)JoJo1973 Wrote:  
Code:

{ 1 2 { 3 4 { 5 } } « →STR » MAPLIST→
returns
Code:

"1"
"2"
{ "3" "4" { "5" } }
instead of
Code:

"1"
"2"
"3"
"4"
"5"

It seems to me that since the program is mapped to all elements thru full depth then all the sublists should be exploded, but maybe that's a design choice and not a bug.

Claudio could you clarify?

MAPLIST→ is simply a shortcut for « MAP LIST→ DROP », in other words, it does MAP but doesn't recreate the list, simply leaves the items on the stack. This saves time when you don't actually need the list created. Creating the list has a cost in time and memory, as all objects must be copied within the new list object.
I can't recall where exactly I needed this (maybe a solver, or symbolics rule processing, menus, etc.) so I created this command to improve performance.

EDIT: I wanted to clarify that going into the sub-lists individual elements while still retaining the structure is a feature of MAP, coming directly from the 50g AUR. LIST→ on the other hand, does not recurse.
Find all posts by this user
Quote this message in a reply
Post Reply 




User(s) browsing this thread: