HP Forums

Full Version: HP 48GX IFT Error: Too Few Arguments
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Program: CURVE

\<< { 6 7 8 9 } CF {
{ "Δ"
\<< 'D' STO 6 SF 9 FS? CALC IFT 9 SF \>> }
{ "R"
\<< 'R' STO 7 SF 9 FS? CALC IFT 9 SF \>> }
{ "T"
\<< 'T' STO 8 SF 9 FS? CALC IFT 9 SF \>> }
} TMENU
\>>

@ D is for delta (central angle).
@ R is for radius.
@ T is for tangent length.
@ If user flag 9 is set, program executes CALC and the 2 selected curve
parts are used to calculate the remaining 6 parts.

Input example:

30, press menu key below Δ

Output:

@ Executes CALC
@ Displays
IFT Error:
Too Few Arguments
@ 1: 0

Upon entering the first curve part, the program should not execute CALC because user flag 9 is clear; the program returns 0 to stack level 1. Why "IFT Error: Too Few Arguments?" I'm following the IFT command syntax. What am I missing?
(06-17-2021 01:46 AM)MNH Wrote: [ -> ]I'm following the IFT command syntax.

You are not. For IFT to work properly, the then-sequence, that is, in your case, the CALC program, must be present on the stack as an argument. So, instead of evaluating CALC, you have to recall it to the stack, by doing 'CALC' RCL. If the flag is true, IFT will evaluate the program; otherwise, the program will simply be dropped from the stack.

Anyhow, in the present context, there is no particular advantage to using the RPN command form rather than the normal IF 9 FS? THEN CALC END form; the latter would even be shorter by 2.5 bytes.
Instead of doing 'CALC' RCL, you can simply replace CALC with 'CALC' (the former executes the CALC program while the latter places the program's variable onto the stack, which is then evaluated by the IFT command if necessary).
(06-17-2021 06:05 AM)Han Wrote: [ -> ]while the latter places the program's variable onto the stack

It's strange to me that single quotes around a variable are necessary to store (STO command) a variable, whereas it can also place a program's variable onto the stack.
(06-17-2021 03:54 AM)Giuseppe Donnini Wrote: [ -> ]For IFT to work properly, the then-sequence, that is, in your case, the CALC program, must be present on the stack as an argument.

If I wrap CALC in program delimiters, the IFT command works as expected. Why?
(06-18-2021 12:32 AM)MNH Wrote: [ -> ]
(06-17-2021 03:54 AM)Giuseppe Donnini Wrote: [ -> ]For IFT to work properly, the then-sequence, that is, in your case, the CALC program, must be present on the stack as an argument.

If I wrap CALC in program delimiters, the IFT command works as expected. Why?

Consider the difference between running the following three programs:

(1) << 1 2 + >>
(2) << '1+2' >>
(3) << << 1 2 + >> >>

If the results are not obvious, try them on your RPL machine. That's why executing the program << CALC >> is different from executing the program << << CALC >> >>. The inner program delimiters in the second program cause the inner program to be placed on the stack unevaluated.
(06-17-2021 06:05 AM)Han Wrote: [ -> ]Instead of doing 'CALC' RCL, you can simply replace CALC with 'CALC' (the former executes the CALC program while the latter places the program's variable onto the stack, which is then evaluated by the IFT command if necessary).

I know, but I don't like this solution, and here is why.

IFT and IFTE were explicitly designed to allow the use of lists in lieu of programs. As a consequence, IFT and IFTE do not execute, but evaluate their argument(s) – or, in system RPL parliance, they COMPEVAL their argument(s) if applicable, instead of simply EVAL’ing them. Therefore, if you don’t delete the RCL, your program will work whether the variable contains a real program or a list as program. Otherwise, you would deprive yourself of that possibility, or even risk breaking the code later on – for the sake of 2.5 bytes.
(06-19-2021 01:45 AM)Giuseppe Donnini Wrote: [ -> ]IFT and IFTE were explicitly designed to allow the use of lists in lieu of programs.

I thought they were designed for programs written in stack syntax. I like their compact form.
(06-20-2021 09:39 PM)MNH Wrote: [ -> ]
(06-19-2021 01:45 AM)Giuseppe Donnini Wrote: [ -> ]IFT and IFTE were explicitly designed to allow the use of lists in lieu of programs.

I thought they were designed for programs written in stack syntax. I like their compact form.

They are. What Giuseppe is saying is that the arguments to IFT/IFTE can be in the form of lists instead of programs (they can also be null-tagged commands).

Some examples:

Code:

x > { 2 * 1 - } { 2 * 1 + } IFTE

x > :: SWAP :: DROP IFTE

The advantage is that lists and null tags execute faster and take less memory than programs. Unfortunately, RPL commands are inconsistent in terms of what procedure-class objects they will accept. DOSUBS, MAP and STREAM will accept null tags while DOLIST will not. Neither will accept lists.
Thanks for chiming in, John. That’s exactly what I meant. I wasn’t refering to the outer program which contains the conditional branching structure, but to the elements of that structure themselves, i.e. to the then-sequence and, possibly, the else-sequence. In the case of the RPN command forms, these sequences are indeed represented by stack objects, and they can be of any type. However, since IFT and IFTE evaluate their stack arguments instead of just executing them, list objects can be used as programs.

Note that the RPN forms also allow for more sophisticated programming in yet another respect: since all they care about is the stack, the test-sequence, the then-sequence, and the else-sequence may be placed in separate programs. In contrast, the ordinary non-RPN structures require that everything be contained in the same (outer) program.

(06-23-2021 02:33 PM)John Keith Wrote: [ -> ]Unfortunately, RPL commands are inconsistent in terms of what procedure-class objects they will accept.

All reliable sources, like the HP-28C Reference Manual (which presented the concepts of RPL to the general public for the first time), Bill Wickes’ Insights series, the HP Journal articles, RPLMAN.DOC, etc., make it abundantly clear that lists and tagged objects are not procedure class objects, but data class objects. Try to evaluate a list on the HP-28C/S!

It is true that the HP-48 added the possibility to make lists behave like procedure-class objects, but only for two very specific reasons, and only on explicit request by EVAL or IFT/IFTE, precisely. These reasons are:

  1. To allow procedures to build new procedures. Otherwise, this essential RPL feature would not be available at the user level, since – for good reasons – there is no \->PRG / PRG\-> pair of commands for building and taking apart compiled program objects, similar to \->LIST and LIST\-> for lists.
     
  2. To have a simple means of changing directories by evaluating path lists.

But for all other purposes, lists are simply data class objects. So, the behavior you describe here

(06-23-2021 02:33 PM)John Keith Wrote: [ -> ]Neither will accept lists.

is the (well) documented behavior.


(06-23-2021 02:33 PM)John Keith Wrote: [ -> ][...] they can also be null-tagged commands [...] The advantage is that lists and null tags execute faster and take less memory than programs.

I would not recommend this practice for the reasons given above. If time and space are critical, the programmer should definitely turn to System RPL or assembly language, instead of writing bad, and potentially unstable, code.

N.B. Since the original poster explicitly referred to the HP-48GX, I want to add that MAP is not an HP-48 command, and that DOLIST can indeed handle tagged objects on the HP-48. As so often, the HP-49G series introduced a problem that never existed on the HP-48 (in this case, by botching up the argument dispatching mechanism).
Thanks Giuseppe and John, for the patient and detailed comments on this interesting, even if subtle, topic. Even when one thinks RPL is mostly understood, subtle issues like this are reminders to constantly review the basics. At least for me... Smile
(06-19-2021 01:45 AM)Giuseppe Donnini Wrote: [ -> ]
(06-17-2021 06:05 AM)Han Wrote: [ -> ]Instead of doing 'CALC' RCL, you can simply replace CALC with 'CALC' (the former executes the CALC program while the latter places the program's variable onto the stack, which is then evaluated by the IFT command if necessary).

I know, but I don't like this solution, and here is why.

IFT and IFTE were explicitly designed to allow the use of lists in lieu of programs. As a consequence, IFT and IFTE do not execute, but evaluate their argument(s) – or, in system RPL parliance, they COMPEVAL their argument(s) if applicable, instead of simply EVAL’ing them. Therefore, if you don’t delete the RCL, your program will work whether the variable contains a real program or a list as program. Otherwise, you would deprive yourself of that possibility, or even risk breaking the code later on – for the sake of 2.5 bytes.

IFT was neither explicitly designed to make use of lists, nor does it exclude it. The argument check allows for either a real number and any object, or a symbolic class object and any object. In the latter case, IFT will try to resolve the object on stack level 2 first, then calls itself again, so that ultimately the core routine expects a T/F (real number) value on stack level 2 and any object on stack level 1. The object on stack level one is passed to the user command EVAL.

As for whether to use 'CALC' RCL or simply CALC, that all depends on the content of the variable. If CALC itself is a list, then both CALC and 'CALC' RCL will behave the same way within the runstream -- they will both place the stored list onto the stack to be passed to IFT. IFT then uses user command EVAL, which in turn uses the internal command COMPEVAL on the list and treat the list as a program. On the other hand, if CALC is an ordinary program, then 'CALC' and 'CALC' RCL will behave the same way when passed to the IFT command, which uses the internal EVAL command (not to be confused with the user command EVAL).

The idea that one should or should not use RCL is really dependent on the user. If you are one who happens to prefer using lists as programs as opposed to the actual program object specifically designed for programs, then you will certainly want to either use the CALC or 'CALC' RCL method. However, I personally would not consider this the "designed" use of lists since there already exists an actual program object. In fact, one could argue that this is a quirk that requires care. For example (assuming CALC contained a list), if one wanted IFT to return the list contained inside the variable CALC, then ONLY 'CALC' followed by IFT would achieve this goal, as either CALC or 'CALC' RCL would effectively run the list's contents as a program upon reaching the IFT command.

As for the speed argument, any speed gain would be so little as to be unnoticeable by the user. (A user program merely adds the << and >> delimiters, which contain internal commands that essentially serve as error traps for the ON key.) The evaluation of lists uses COMPEVAL, which passes control over to the assembly routine DOCOL, which is exactly the same assembly routine for handling programs.

In practice, there is really no reason to use lists in place of a program unless you have a particular programming paradigm you personally follow.
(06-24-2021 11:02 PM)Han Wrote: [ -> ]IFT was neither explicitly designed to make use of lists, nor does it exclude it.

This interpretation is solely based on the internal dispatch mechanism, which you described very well, but there remains the fact that IFT and IFTE are the only (!) commands on the HP-48 (besides EVAL itself) that treat lists as procedures, and that is significant enough.

Furthermore, IFT and IFTE are obviously designed to use stack arguments.

So my point is that, in the absence of any specific context, the standard approach should respect these two premises: it should not preclude the occasional use of lists as programs, and it should put the actual arguments on the stack, not their labels — because identifier objects do not distinguish between execution and evaluation. Hence my recommendation to use 'CALC' RCL.

Naturally, there are cases where a non-standard approach is mandatory, for example, if you want IFT / IFTE to treat lists as data class objects.

(06-24-2021 11:02 PM)Han Wrote: [ -> ]As for the speed argument

I never raised any speed argument.

(06-24-2021 11:02 PM)Han Wrote: [ -> ]A user program merely adds the << and >> delimiters

A user program may add a lot more than just the \<< and \>> delimiters, which results in a number of subtle, but significant, differences at the User RPL level between the execution of a program and the evaluation of a list, that is, its execution as a program. (This is, by the way, one of the major reasons why there is no \->PRG / PRG\-> pair of commands for building and taking apart compiled program objects, as I mentioned earlier.)

  1. Programs within programs are automatically quoted, whereas programs within lists are not. Therefore, running a program which contains another program, and running the seemingly equivalent list as a program, produce different results.

    Example 1:

    \<< 1 \<< 2 \>> 3 \>> EVAL --> 1 \<< 2 \>> 3
      { 1 \<< 2 \>> 3 }   EVAL --> 1 2 3

    Example 2:

    \<< 1 \<< A \>> 3 \>> EVAL --> 1 \<< A \>> 3

      { 1 \<< A \>> 3 }   EVAL --> 1 'A' 3   ( if variable A is undefined )
      { 1 \<< A \>> 3 }   EVAL --> 1 2 3     ( if variable A is defined and
                                               contains e.g. the real number 2 )
     
  2. Within a program — just like within the command line —, global and local names can be quoted by using tick delimiters in order to postpone their execution. By contrast, quoted names within a list are automatically de-quoted as soon as the list gets compiled by the parser.

    Example:

    \<< 'A' \>> --[ENTER]--> \<< 'A' \>> EVAL --> 'A'

      { 'A' }   --[ENTER]-->    { A }    EVAL --> 'A'   ( if A is undefined )
      { 'A' }   --[ENTER]-->    { A }    EVAL --> 2     ( if A contains e.g.
                                                          the real number 2 )
     
  3. If you evaluate a list containing a HALT or a PROMPT command, the execution flow will be suspended at the appropriate place, just like within a program. However, any attempt to single-step from there through the remainder of the list will immediately restart normal execution. In other words, pressing {SST}, {SST\|v}, or even {NEXT}, has the same effect as pressing [LS]+[CONT].

The third point is of minor importance (I just mention it here for the sake of completeness), but ignoring the first two idiosyncrasies may easily lead to unexpected errors.

The reason for the observed discrepancy lies in the different way in which the parser compiles programs as opposed to lists, with the result that the same source text might be turned into different binary data depending on whether it gets embedded in a program or in a list.
(06-25-2021 10:51 AM)Giuseppe Donnini Wrote: [ -> ]This interpretation is solely based on the internal dispatch mechanism, which you described very well, but there remains the fact that IFT and IFTE are the only (!) commands on the HP-48 (besides EVAL itself) that treat lists as procedures, and that is significant enough.

Wouldn't this singularity alone be sufficient to say that lists are not to be used in place of programs?

Quote:Furthermore, IFT and IFTE are obviously designed to use stack arguments.

So my point is that, in the absence of any specific context, the standard approach should respect these two premises: it should not preclude the occasional use of lists as programs, and it should put the actual arguments on the stack, not their labels — because identifier objects do not distinguish between execution and evaluation. Hence my recommendation to use 'CALC' RCL.

I do agree that the actual arguments onto the stack (as opposed to references by variable names) is the least ambiguous method in light of the special behavior of list handling.

Quote:I never raised any speed argument.

My apologies; that wasn't meant to be directed at you but someone did mention speed differences and I should have been more careful to separate my repsonse.

Quote:A user program may add a lot more than just the \<< and \>> delimiters, which results in a number of subtle, but significant, differences at the User RPL level between the execution of a program and the evaluation of a list, that is, its execution as a program. (This is, by the way, one of the major reasons why there is no \->PRG / PRG\-> pair of commands for building and taking apart compiled program objects, as I mentioned earlier.)

** examples removed for the sake of brevity ***

Programs and lists are not executed differently at all by IFT (or more precisely, by the internal COMPEVAL command). Both object types are handled by the assembly command DOCOL. Therefore, they CANNOT behave differently... unless they are actually different. Seems silly of a statement, but allow me to explain.

The reason for the difference in the behavior is not how they are executed, but actually how they are compiled. When one creates a list, such as in your example { 'A' }, the command line compiles it as { A }. Therefore, the program << A >> and the list { A } are executed the same way. On the other hand, entering the program << 'A' >> and the list { 'A' } result in two differently compiled objects. For << 'A' >>, the command line compiles the delimiters around the A into a pair of quote commands whereas the command line compiles { 'A' } into a container with a single element (a variable, but unquoted). It may seem like I'm nitpicking, but I do want to emphasize here that the behavior of the evaluation is exactly the same, provided that they are in fact the same. Your examples, on the other hand, are examples of how two similar objects (program vs list) are not parsed and compiled the same way by the command line even though they are typed in the exact same manner, excluding the containing delimiters << >> and { }. Thus the behavior is different because they are not actually compiled as the same sequence of objects, not because one is a program and the other is a list containing seemingly the same "things" inside (because they do not).

Said differently, on the SysRPL level, a program << 'A' >> would look like:
Code:
::
  x<<
  x' 
  ID A
  xENDTIC
  x>>
;

whereas a list typed as { 'A' } (but compiled as { A }) would look like:

Code:
{
  ID A
}

in SysRPL code. On the other hand

Code:
{
  x'
  ID A
  xENDTIC
}

would appear on the stack as { 'A' } and evaluate exactly as << 'A' >>. If it were feasible to create a list that contains the exact same contents of a program (as compiled by the command line), then they would behave the same way (excluding possibly your point #3, depending on whether you want to include the << and >> commands in your list, as they provide ON/HALT traps). However, it is actually not possible to create the last list without special tools. Thus, the idea that lists can be used as a program turns into an exercise of careful tip-toeing around the differences in how lists and programs are compiled, and not how they are EVAL'd (via the user command EVAL).

EDIT: The same applies to your example #1 -- there is an invisible command xSILENT' that is not present in the list { 1 2 << 3 4 >> }

EDIT #2: Also, in the context of discussing IFT and EVAL (user commands), there is no difference between executing and evaluating. IFT calls EVAL, and EVAL eventually passes control over to DOCOL for lists, which is also the exact same behavior for programs (DOCOL is also used). As for the reason we do not have ->PRG and PRG->, it again has to do with how lists and programs are compiled. Lists get compiled without ever inserting "invisible" or even "similar-looking" objects like programs do (and programs do this by necessity). Thus, OBJ-> and ->LIST exist as commands because there are never instances of creating an invalid list from objects obtained by the OBJ-> commands. This is not true for programs. If one were able to "explode" a user program made from the ordinary command line, one could then put it back together in a manner that would cause the calculator to crash. This can never happen with lists created from a user-made list using only the ordinary command line.
(06-25-2021 09:45 PM)Han Wrote: [ -> ]I do agree that the actual arguments onto the stack (as opposed to references by variable names) is the least ambiguous method in light of the special behavior of list handling.

Thanks. I hope this settles the debate.

My last point was nothing more than a spontaneous response to your assertion that “a user program merely adds the << and >> delimiters”. In my mind, it was totally unrelated to the execution vs. evaluation debate. My apologies for not making this more clear. On the other hand, your whole post leaves the impression that I just had to mention a few keywords and you reeled off a whole System RPL lecture without even reading my post. Otherwise, you would have noticed that I already made it unmistakably clear that

Quote:The reason for the observed discrepancy lies in the different way in which the parser compiles programs as opposed to lists.

So, you are pushing at an open door here. In fact, I was going to underpin my arguments with a solid layer of System RPL theory (the first paragraph of which is still in my notepad, see below), but I refrained from doing so because, after all, it seemed too esoteric in the context of this particular thread.

Quote:[...] whether it gets embedded in a list or in a program. To see this in effect, we must, for a moment, delve into the system’s representation of what we see at the user’s level.

If we reconsider the objects above and compare their respective bodies after they have been compiled, we find the following differences:

Examples 1 & 2

+-------------------------------------+-----------------+-----------------+
|                                     | \<<             | {               |
|                                     |   1             |   1             |
|             SOURCE TEXT             |   \<<           |   \<<           |
|                  &                  |     2 @ resp. A |     2 @ resp. A |
|           DECOMPILED FORM           |   \>>           |   \>>           |
|                                     |   3             |   3             |
|                                     | \>>             | }               |
+-------------------------------------+-----------------+-----------------+
|                          Prologue   | ::              | {               |
|                                  /  |   x<<           |                 |
|                                 |   |   %1            |   %1            |
|                                 |   |   xSILENT'      |                 |
|                                 |   |   ::            |   ::            |
|     COMPILED FORM        Object |   |     x<<         |     x<<         |
| ( System RPL Syntax )     Body  |   |     %2 ( ID A ) |     %2 ( ID A ) |
|                                 |   |     x>>         |     x>>         |
|                                 |   |   ;             |   ;             |
|                                 |   |   %3            |   %3            |
|                                  \  |   x>>           |                 |
|                          End Marker | ;               | }               |
+-------------------------------------+-----------------+-----------------+
(06-26-2021 01:07 AM)Giuseppe Donnini Wrote: [ -> ]
Quote:The reason for the observed discrepancy lies in the different way in which the parser compiles programs as opposed to lists.

So, you are pushing at an open door here. In fact, I was going to underpin my arguments with a solid layer of System RPL theory (the first paragraph of which is still in my notepad, see below), but I refrained from doing so because, after all, it seemed too esoteric in the context of this particular thread.

Indeed my mind was so particularly focused on what you wrote earlier (that lists and programs were "handled" -- for lack of better word -- differently) that I completely glossed over the last parts of your post. Again, my apologies. (And thank you for holding the door for me! :-)
Han, I am a late-comer to the world of System RPL and Saturn Assembly, and if I am any good at it, it is – at least in part – also due to the many outstanding contributions you have made in the field since the now legendary comp.sys.hp48 days. So I truly hate being in disagreement with you over an artificial problem, but I somehow had to stand my ground here. To be honest, I miss your contributions to the world of RPL and deplore your “defection” to the Prime camp. Smile
(06-26-2021 08:30 AM)Giuseppe Donnini Wrote: [ -> ]Han, I am a late-comer to the world of System RPL and Saturn Assembly, and if I am any good at it, it is – at least in part – also due to the many outstanding contributions you have made in the field since the now legendary comp.sys.hp48 days. So I truly hate being in disagreement with you over an artificial problem, but I somehow had to stand my ground here. To be honest, I miss your contributions to the world of RPL and deplore your “defection” to the Prime camp. Smile

I, for one, am happy there was a disagreement between you guys; the discussion was interesting and educational, and no doubt enlightened many readers. I hope more such points are debated in the future. Smile
(06-26-2021 08:30 AM)Giuseppe Donnini Wrote: [ -> ]Han, I am a late-comer to the world of System RPL and Saturn Assembly, and if I am any good at it, it is – at least in part – also due to the many outstanding contributions you have made in the field since the now legendary comp.sys.hp48 days. So I truly hate being in disagreement with you over an artificial problem, but I somehow had to stand my ground here. To be honest, I miss your contributions to the world of RPL and deplore your “defection” to the Prime camp. Smile

Haha -- our disagreements turned out to actually be agreements. You had kindly held the door open with one hand and welcomed me through with the other, yet I kept standing there knocking. I should have been more careful with my reading. My contributions to RPL are quite limited these days. Plus, I was fortunate to have the privilege of standing on the shoulder of giants who had already paved much of the way before I even got my HP48. The last major work I was able to contribute was updating Jazz to the HP50 series and that has been years. Nostalgia does pull me back from time to time, as do discussions here on the forums. I'm grateful to the site and its users for providing a rich environment for such discussions.

As for the Prime, I'm hoping to be able to show off a new project that might actually bring things full circle back to RPL. But that's all I can say for now :-)
(06-26-2021 05:04 PM)Han Wrote: [ -> ]As for the Prime, I'm hoping to be able to show off a new project that might actually bring things full circle back to RPL. But that's all I can say for now :-)

You may be paving the way for some more sales of Prime units, Han. Be careful what you say! Smile
Reference URL's