In Racket, there are two Generic Number procedures named add1
and sub1
, equivalent to (curry + 1)
and (curryr - 1)
respectively.
Are these procedures for stylistic use or do they have some sort of optimization benefit? What is the history behind these procedures?
The history?
We need go to back in time to 1986, when the Revised Revised Report on Scheme was discussed.
Dan Friedman wrote:
When describing arithmetic I find the symbols "1+" and "-1+" very harmful. The function + is denoted by the symbol "+" and this confuses people who are trying to understand the primitive recursive definition of +. Purely for pedagogical reasons I would like to see "add1" and "sub1" be optional. I know that macscheme & PC-Scheme have included them. I don't like multiple names for the same construct, but we have a few instances already in the report.
See more here:
https://groups.csail.mit.edu/mac/ftpdir/scheme-mail/HTML/rrrs-1986/msg00246.html https://groups.csail.mit.edu/mac/ftpdir/scheme-mail/HTML/rrrs-1986/msg00251.html
It turns out the name add1
didn't make it into the minimal standard, but some (non-minimal) implementations included it (such as Chez Scheme).
According to Practical Scheme[1], the implementations Chez Scheme, Racket, and, Chicken Scheme include add1
as standard. I am sure there are other implementations too:
[1] http://practical-scheme.net/wiliki/schemexref.cgi?add1
As for its use: It's more convenient (and easier to read)
(map add1 xs)
than
(map (lambda (x) (+ x 1)) xs)
These days there ought to be no difference in performance (but don't take my word on it - make a benchmark). Once upon a time a simple compiler might produce better code for the first expression. A decent compiler should produce the same output for both expressions.
According to the Racket Docs it is syntactic suger:
Incrementing (adding 1) and decrementing (subtracting 1) are somewhat common in programming. Using names for them is intended to make it easier for someone reading code to see at a glance which of the two operations is intended.
Performance: Using the native '+' is the best way to go performance wise. 'Generic arithmetic operations'(+,-,<,> etc...) are inlined by the JIT compiler racket docs:
Inlined fixnum and flonum arithmetic operations are among the most important advantages of the JIT compiler. For example, when + is applied to two arguments, the generated machine code tests whether the two arguments are fixnums, and if so, it uses the machine’s instruction to add the numbers (and check for overflow).
The performance difference is 'so small it is difficult to detect', from scheme JIT compiler:
The JIT compiler works incrementally as functions are applied, but the JIT compiler makes only limited use of run-time information when compiling procedures, since the code for a given module body or lambda abstraction is compiled only once. The JIT’s granularity of compilation is a single procedure body, not counting the bodies of any lexically nested procedures. The overhead for JIT compilation is normally so small that it is difficult to detect.
Some cons of adding a definiton to the global environment: Memory usage, Namespace clashes, Increase GE maintanance.
Some pros of adding a definiton to the global environment: Readability, easier maintenance, reusable code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With