2023/09/29
Real numerical functions
Scheme devines a set of elementary functions that can handle complex numbers.
In Gauche, complex elementary functions is built on top
of real domain functions. Up to 0.9.12, we had real-only version
with the name such as %sin
or %exp
. As the percent prefix
suggests, they are not meant to be used directly; sin
ro exp
are built on top of them.
However, sometimes you want to use real-only versions to avoid overhead
of type testing and dispatching complex numbers. srfi:94 defines
real-domain functions, so we decided to adapt them. Now you have real-sin
,
real-exp
etc. (draft:real-exp) as built-in.
Note that scheme.flonum
also provides "flonum-only"
version of elementary functions, e.g. flsin
(ref:scheme.flonum).
They won't even accept exact numbers. Since it is in R7RS-large,
you may want to use them for portable code.
Although the names %sin
etc. are undocumented and not meant to be
directly used, they were visible by default, so some existing code
are relying on it. It needs some effort to rewrite all occurrences
of such functions with the new real-sin
etc, so we provide
a compatibility module, compat.real-elementary-functions
. Just
using it in your code provides compatibility names. If you want
to make your code work on both 0.9.12 and 0.9.13, you can use
cond-expand
:
(cond-expand ((library compat.real-elementary-functions) (use compat.real-elementary-functions)) (else))
Tags: 0.9.13, NumericFunctions
2023/09/28
Pretty print indentation
Yet another small thing good to have. You can now specify base indentation to the pretty printer (ref:pprint). It is applied to the second line and after.
gosh> (pprint (make-list 100 'abc) :indent 20) (abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc) #<undef>
To say more precisely, when the pretty printer spills data to another line, it inserts "a newline + whitespace * indent", plus any indent computed by the pretty printer.
The benefit easiest to see is when the pretty printer is used inside
format
. When a pretty printing triggered by the ~:w
directive,
it sets the base indentation at the column it starts printing. Hence
the entire pretty print is indented to align nicely:
gosh> (format #t "Long list: ~:w\n" (make-list 100 'abc)) Long list: (abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc) #<undef>
Since pretty printing is built-in to the core printer (pprint
is just
a simple interface to use), other output routines such as write
can
also use base indentation. You can set indent
slot of a write-controls.
gosh> (write (make-list 100 'abc) (make-write-controls :pretty #t :indent 20 :width 79)) (abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc)#<undef>
Tags: 0.9.13, pretty-printing
2023/09/26
Segmented completion
Another little something on REPL. It can now complete symbols like call-with-current-continuation
from c-w-c-c
. This is an old tradition of
Lisp environment.
We added a new module text.segmented-match
(draft:text.segmented-match)
to support this.
2023/09/26
Hints for unbound variable error
While working on REPL, sometimes you accidentally try to evaluate a variable that isn't
visible from your current module.
It is a bit annoying if you know the module is loaded, just you forget
to use
it in the current module.
So we added a little feature. When REPL reports an unbound varriable error, it also lists if there are variables of that name, exported from modules that are loaded into the process but not visible from the evaluating module:
gosh$ (thread-start! (make-thread (^[] (print "Hi")))) *** UNBOUND-VARIABLE-ERROR: unbound variable: make-thread NOTE: `make-thread' is exported from the following module: - gauche.threads Stack Trace: _______________________________________ 0 (report-error e) 1 (make-thread (^ () (print "Hi"))) at "(input string port)":1
It may be nice to show modules that aren't even loaded, too, but that would be too costly so we avoided it. It also doesn't show non-exported variables, which is debatable--sometimes you forgot to export one and that caused this error. Let's use this for a while and see if we need non-exported ones, too.
This is realized in a general mechanism in error reporting. We haven't documented it yet, for we may tweak the interface, but I'll show it to give the general idea.
The error message in REPL, including the stack trace, is produced by
report-error
(ref:report-error). It prints *** ...
line,
with the condition class name and error message, then calls
a generic function report-additional-condition
on the condition.
We have a specialized method for <unbound-variable-error>
which
searches the name in the loaded modules and prints the hint.
If the thrown condition is a compound condition, report-additional-condition
is called over each component of the compound condition. This allows
custom report for each component. When you load a file that has a statically
detectable error, you get the additional information (While compiling ...
).
It is also realized by the same mechanism. The compiler and the loader
adds the location information as a compound condition, and report-error
calls report-additional-condition
on them, which shows those additional
messages.
gosh> ,l ./foo *** ERROR: wrong number of arguments: cons requires 2, but got 1 While compiling "./foo.scm" at line 1: (define (bar x) (cons x)) While loading "./foo.scm" at line 2 Stack Trace: _______________________________________ 0 (report-error e) 1 (errorf "wrong number of arguments: ~a requires ~a, but got ~"... 2 (pass1/expand-inliner program id gval cenv)
Tags: 0.9.13, REPL, report-error
2023/04/13
:immutable
slot option
Immutability is far more valued nowadays than when CLOS was designed. Back then, basically the system allows programmers to change almost everything, assuming he knows what he was doing.
However, specifying something immutable is not only to protect users from shooting their own foot; it communicates to the readers. Here the readers can be a programmer who reads and uses the code years later (who can be the same programmer who wrote it bot has forgotten the details), or a program-processing programs such as optimizer to take advantage of immutablity.
CLOS (and Gauche's object system) have enough flexibility to implement immutable slots, but it is somewhat awkward. It's not as simple as having a custom setter that throws an error; for, the slot setter is also called in the instance initializer which runs at the instance construction. You have to distinguish whether the slot setter is invoked during initialization or outside initialization, but such dynamic introspection would be costly.
We came up an alternative mechanism which is effectively realizes immutable slots in practical situations, but does not require to distinguish whether it's in initialization or not.
If a slot has a true value for :immutable
slot option, the slot can only
be initialized once--that is, the setter sets the value if the slot is previously
unbound, but throws an error if not. If you give the slot an initial value,
either with :init-keyword
or :init-value
etc., then that one chance
to set the value is used within initializer. Uninitialized immutable slots
don't make much sense, so we expect almost always immutable slots are initialized
this way.
It is possible that the initializer leaves the slot unbound, and later
the user call slot-set!
to set it once. It can be viewed as delayed
initialization.
(We first named this option init-once
, for the slot can be set once,
but changed our mind for it could be confusing.)
Tag: ObjectSystem
Comments (0)