Skip to content

Commit e67c88e

Browse files
authored
Merge pull request #434 from sogaiu/tweak-scopes-section
Cover more material in scopes section
2 parents d51c8f7 + 96671b0 commit e67c88e

1 file changed

Lines changed: 102 additions & 28 deletions

File tree

content/docs/bindings.mdz

Lines changed: 102 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -39,50 +39,124 @@ also add documentation to a function by passing a string to the @code`def` or
3939

4040
### Scopes
4141

42-
Defs and vars (collectively known as bindings) live inside what is called a
43-
scope. A scope is simply where the bindings are valid. If a binding is
44-
referenced outside of its scope, the compiler will throw an error. Scopes are
45-
useful for organizing your bindings and they can expand your programs. There
46-
are two main ways to create a scope in Janet.
42+
Defs and vars (i.e. bindings) live inside what is called a scope. A
43+
scope is simply where the bindings are valid. If a binding is
44+
referenced outside of its scope, the compiler will throw an error.
45+
Scopes are useful for organizing your bindings and can provide
46+
flexibility in expressing your programs. There are two main ways to
47+
create a scope in Janet.
4748

48-
The first is to use the @code[do] special form. @code[do] executes a series of
49-
statements in a scope and evaluates to the last statement. Bindings created
50-
inside the form do not escape outside of its scope.
49+
The first is to use the @code[do] special form. @code[do] executes a
50+
series of statements in a scope and evaluates to the last statement.
51+
Bindings created inside the form do not escape outside of its scope.
5152

5253
@codeblock[janet](```
5354
(def a :outera)
5455

5556
(do
5657
(def a 1)
5758
(def b 2)
58-
(def c 3)
59-
(+ a b c)) # -> 6
59+
(+ a b)) # -> 3
6060

6161
a # -> :outera
62-
b # -> compile error: "unknown symbol \"b\""
63-
c # -> compile error: "unknown symbol \"c\""
62+
63+
# compile error: unknown symbol b
64+
b
65+
```)
66+
67+
Any attempt to reference the bindings created within the @code`do`
68+
form after it has finished executing will fail.
69+
70+
Note that in the example above, defining @code[a] inside the @code[do]
71+
form did not overwrite the original definition of @code[a] for the
72+
global scope. As the code in the example demonstrates, the original
73+
definition of @code[a] was still accessible after the @code[do] form.
74+
We say that @code[a] or its binding was "shadowed" within the
75+
@code[do] form.
76+
77+
Another way to create a scope is to create a closure. The @code[fn]
78+
special form does this by introducing a scope just like the @code[do]
79+
special form.
80+
81+
@codeblock[janet](```
82+
(def a :first!)
83+
a # -> :first!
84+
85+
(def f
86+
(fn []
87+
# a new scope has been introduced
88+
(def b a)
89+
# shadowing a's outer binding
90+
(def a :inner!)
91+
# after the following, the new scope "ends"
92+
@[b a]))
93+
94+
# binding created in f's scope are not valid here
95+
a # -> :first!
96+
# compile error: unknown symbol b
97+
b
98+
99+
# f can access those bindings though
100+
(f) # -> @[:first! :inner!]
101+
102+
# shadow a's original binding
103+
(def a :second!)
104+
a # -> :second!
105+
106+
# f's bindings unaffected by immediately previous shadowing
107+
(f) # -> @[:first! :inner!]
64108
```)
65109

66-
Any attempt to reference the bindings from the @code`do` form after it has
67-
finished executing will fail. Also notice that defining @code[a] inside the
68-
@code`do` form did not overwrite the original definition of @code[a] for the
69-
global scope.
110+
It also possible to create new scopes using the @code[if] and
111+
@code[while] special forms. In both cases, bindings can be introduced
112+
in their respective "condition" portions and the bindings will be
113+
valid within the rest of each of the forms.
114+
115+
@codeblock[janet](```
116+
# using def in if's condition
117+
(defn f [x]
118+
(if (def n x)
119+
n
120+
(when (not n) :was-nil!)))
121+
122+
(f :something) # -> :something
70123

71-
The second way to create a scope is to create a closure. The @code[fn] special
72-
form also introduces a scope just like the @code[do] special form.
124+
(f nil) # -> :was-nil!
73125

74-
There is another built in macro, @code[let], that does multiple @code`def`s at
75-
once, and then introduces a scope. @code[let] is a wrapper around a combination
76-
of @code`def`s and @code`do`s, and is the most "functional" way of creating
77-
bindings.
126+
# using def in while's condition
127+
(def arr @[2 1 0])
128+
(while (def n (length arr))
129+
(array/pop arr)
130+
(when (zero? n) (break)))
131+
132+
arr # -> @[]
133+
```)
134+
135+
There is also a built-in macro, @code[let], that does multiple
136+
@code`def`s at once, and then introduces a scope. @code[let] is a
137+
wrapper around a combination of a @code[do] and @code[def]s, and is
138+
the most "functional" way of creating bindings.
78139

79140
@codeblock[janet](```
80141
(let [a 1
81-
b 2
82-
c 3]
83-
(+ a b c)) # -> 6
142+
b 2]
143+
(+ a b)) # -> 3
84144
```)
85145

86-
The above is equivalent to the example using @code[do] and @code[def]. This is
87-
the preferable form in most cases. That said, using @code`do` with multiple
88-
@code`def`s is fine as well.
146+
The above is equivalent to the earlier example using @code[do] and
147+
@code[def].
148+
149+
Note that the special forms @code[def] and @code[var] do not introduce
150+
new scopes and the following sort of code is valid.
151+
152+
@codeblock[janet](```
153+
(def a (def x 1))
154+
155+
a # -> 1
156+
x # -> 1
157+
158+
(var b (var y 2))
159+
160+
b # -> 2
161+
y # -> 2
162+
```)

0 commit comments

Comments
 (0)