[R7RS+] This form has different meanings in the toplevel (without no local bindings) or inside a local scope.
On toplevel, it defines a global binding to a symbol variable. In the first form, it globally binds a symbol variable to the value of expression, in the current module.
(define x (+ 1 2)) x ⇒ 3 (define y (lambda (a) (* a 2))) (y 8) ⇒ 16
The second form is a syntactic sugar of defining a procedure. It is equivalent to the following form.
(define (name . args) body …) ≡ (define name (lambda args body …))
If the form appears inside a local scope (internal define), this introduce a local binding of the variable.
Internal defines can appear in the beginning of body of
or other forms that introduces local bindings. They are equivalent
letrec* form, as shown below.
(lambda (a b) (define (cube x) (* x x x)) (define (square x) (* x x)) (+ (cube a) (square b))) ≡ (lambda (a b) (letrec* ([cube (lambda (x) (* x x x))] [square (lambda (x) (* x x))]) (+ (cube a) (square b))))
Since internal defines are essentially a
you can write mutually recursive local functions, and you can
use preceding bindings introduced in the same scope to calculate
the value to be defined. However, you can’t use a binding that is introduced
after an internal define form to calculate its value; if you do so,
Gauche may not report an error immediately, but you may get strange
errors later on.
(lambda (a) (define x (* a 2)) (define y (+ x 1)) ; ok to use x to calculate y (* a y)) (lambda (a) ;; You can refer to even? in odd?, since the value of even? ;; isn't used at the time odd? is defined; it is only used ;; when odd? is called. (define (odd? x) (or (= x 1) (not (even? (- x 1))))) (define (even? x) (or (= x 0) (not (odd? (- x 1))))) (odd? a)) (lambda (a) ;; This is not ok, for defining y needs to use the value ;; of x. However, you may not get an error immediately. (define y (+ x 1)) (define x (* a 2)) (* a y))
Inside the body of binding constructs, internal defines must appear
before any expression of the same level. The following code isn’t allowed, for
(print a) precedes the
(lambda (a) (print a) (define (cube x) (* x x x)) ; error! (cube a))
It is also invalid to put no expressions but internal defines inside the body of binding constructs, although Gauche don’t report an error.
begin (see Sequencing) doesn’t introduce a new scope.
Defines in the
begin act as if
begin and surrounding
parenthesis are not there. Thus these two forms are equivalent.
(let ((x 0)) (begin (define (foo y) (+ x y))) (foo 3)) ≡ (let ((x 0)) (define (foo y) (+ x y)) (foo 3))
[R7RS base] Expr is evaluated, and each value of the result is bound to each vars. In the first form, it is an error unless expr yields the same number of values as vars.
(define-values (lo hi) (min&max 3 -1 15 2)) lo ⇒ -1 hi ⇒ 15
In the second form, expr may yield as many values as var var1 … or more; the excess values are made into a list and bound to var2.
(define-values (a b . c) (values 1 2 3 4)) a ⇒ 1 b ⇒ 2 c ⇒ (3 4)
In the last form, all the values yielded by expr are gathered to a list and bound to var.
(define-values qr (quotient&remainder 23 5)) qr ⇒ (4 3)
You can use
define is allowed;
that is, you can mix
define-values in internal defines.
(define (foo . args) (define-values (lo hi) (apply min&max args)) (define len (length args)) (list len lo hi)) (foo 1 4 9 3 0 7) ⇒ (6 0 9)
This form is only effective in toplevel.
define, it defines a top-level definition
of variable with the value of expression, but additionally
tells the compiler that (1) the binding won’t change, and
(2) the value of expression won’t change from
the one computed at the compile time.
So the compiler can replace references of variable
with the compile-time value of expression.
An error is signaled when you use
set! to change the value
of variable. It is allowed to redefine variable,
but a warning is printed.
The difference from
define-inline below is that the value of
expression is computed at the compile time and treated as a literal.
Suppose you define x as follows:
(define-constant x (vector 1 2 3))
Then, the code
(list x) is compiled to the same code
(list '#(1 2 3)).
This distinctino is especially important when you do AOT (ahead of time) compilation.
There’s no “internal
define-constant”, since the compiler
can figure out whether a local binding is mutated,
and optimize code accordingly, without a help of declarations.
The second form is a shorthand of
(define-inline variable (lambda formals body …)).
If this appears in the position of internal defines, it is the same as internal defines.
If it appears in the toplevel, it defines an inlinable binding.
An inlinable binding promises the compiler that the binding won’t
change, but unlike constant bindings introduced by
the actual value of expression may be computed at runtime.
Hence the compiler cannot
simply replace the references of variable with the compile-time
value of expression.
However, if the compiler can determine that the value of expression is to be a procedure, it may inline the procedure where it is invoked.
In the example below, the body of
dot3 is inlined where
is called. Furthermore, since the second argument of
a constant vector, you can see
vector-ref on it is computed at
compile time (e.g.
CONST -1.0 etc.)
gosh> (define-inline (dot3 a b) (+ (* (vector-ref a 0) (vector-ref b 0)) (* (vector-ref a 1) (vector-ref b 1)) (* (vector-ref a 2) (vector-ref b 2)))) dot3 gosh> (disasm (^ (dot3 x '#(-1.0 -2.0 -3.0)))) CLOSURE #<closure (#f)> === main_code (name=#f, code=0x28524e0, size=26, const=4 stack=6): signatureInfo: ((#f)) 0 GREF-PUSH #<identifier user#x.20d38e0>; x 2 LOCAL-ENV(1) ; (dot3 x (quote #(-1.0 -2.0 -3.0))) 3 LREF0 ; a 4 VEC-REFI(0) ; (vector-ref a 0) 5 PUSH 6 CONST -1.0 8 NUMMUL2 ; (* (vector-ref a 0) (vector-ref b 0)) 9 PUSH 10 LREF0 ; a 11 VEC-REFI(1) ; (vector-ref a 1) 12 PUSH 13 CONST -2.0 15 NUMMUL2 ; (* (vector-ref a 1) (vector-ref b 1)) 16 NUMADD2 ; (+ (* (vector-ref a 0) (vector-ref b 0)) 17 PUSH 18 LREF0 ; a 19 VEC-REFI(2) ; (vector-ref a 2) 20 PUSH 21 CONST -3.0 23 NUMMUL2 ; (* (vector-ref a 2) (vector-ref b 2)) 24 NUMADD2 ; (+ (* (vector-ref a 0) (vector-ref b 0)) 25 RET
As an extreme case, if both arguments are compile-time constant,
dot3 is completely computed at compile time:
gosh> (disasm (^ (dot3 '#(1 2 3) '#(4 5 6)))) CLOSURE #<closure (#f)> === main_code (name=#f, code=0x2a2b8e0, size=2, const=0 stack=0): signatureInfo: ((#f)) 0 CONSTI(32) 1 RET
The same inlining behavior may be achieved by making
dot3 a macro,
but if you use
dot3 can be used as procedures
(map dot3 list-of-vectors1 list-of-vectors2)
dot3 is a macro you can’t pass it as a higher-order procedure.
The inline expansion pass is run top-to-bottom. Inlinable procedure must be defined before used in order to be inlined.
If you redefine an inlinable binding, Gauche warns you, since the redefinition won’t affect already inlined call sites. So it should be used with care—either use it internal to the module, or use it for procedures that won’t change in future. Inlining is effective for performance-critical parts. If a procedure is called sparingly, there’s no point to define it inlinable.
This form must appear in the toplevel. It creates a global binding of variable in module, which must be either a symbol of the module name or a module object. If module is a symbol, the named module must exist.
Expression is evaluated in the current module.
The second form is merely a syntactic sugar of:
(define-in-module module variable (lambda formals body …))
Note: to find out if a symbol has definition (global binding) in
the current module, you can use
(see Module introspection).