@@ -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
6161a # -> :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