doc/manual: put back the manual in the main repository
[nit.git] / doc / manual / variable.md
1 # Local Variables and Static Typing
2
3 `var` declares local variables. In fact there is no global variable in Nit, so in this document *variable* always refers to a local variable. A variable is visible up to the end of the current
4 control structure. Two variables with the same name cannot coexist: no nesting nor masking.
5
6 Variables are bound to values. A variable cannot be used unless it has a value in all control flow paths (à la Java).
7
8 ~~~
9 var x
10 var y
11 if whatever then
12     x = 5
13     y = 6
14 else
15     x = 7
16 end
17 print x # OK
18 print y # Compile error: y is possibly not initialized
19 ~~~
20
21 ## Adaptive Typing
22
23 Nit features adaptive typing, which means that the static type of a variable can change according to: the assignments of variables, the control flow, and some special operators (`and`, `or`,
24 `or else`, `==`, `!=`, and `isa`).
25
26 ~~~
27 var x # a variable
28 x = 5
29 # static type is Int
30 print x + 1 # outputs 6
31 x = [6, 7]
32 # static type is Array[Int]
33 print x[0] # outputs "6"
34
35 var x
36 if whatever then
37     x = 5
38 else
39     x = 6
40 end
41 # Static type is Int
42 ~~~
43
44 ## Variable Upper Bound
45
46 An optional type information can be added to a variable declaration. This type is used as an upper bound of the type of the variable. When a initial value is given in a variable declaration without a specific type information, the static type of the initial value is used as an upper bound. If no type and no initial value are given, the upper bound is set to `nullable Object`.
47
48 ~~~
49 var x: Int # Upper bound is Int
50 x = "Hello" # Compile error: expected Int
51 var y: Object # Upper bound is Object
52 y = 5 # OK since Int specializes Object
53 var z = 5 # Upper bound is Int
54 z = "Hello" # Compile error: expected Int
55 var t: Object = 5 # Upper bound is Object
56 t = "Hello" # OK
57 ~~~
58
59 The adaptive typing flow is straightforward, therefore loops (`for`, `while`, `loop`) have a special requirement: on entry, the upper bound is set to the current static type; on exit, the upper bound is reset to its previous value.
60
61 ~~~
62 var x: Object = ...
63 # static type is Object, upper bound is Object
64 x = 5
65 # static type is Int, bound remains Object
66 while x > 0 do
67     # static type remains Int, bound sets to Int
68     x -= 1 # OK
69     x = "Hello" # Compile error: expected Int
70 end
71 # static type is Int, bound reset to Object
72 x = "Hello" # OK
73 ~~~
74
75 ## Type Checks
76
77 `isa` tests if an object is an instance of a given type. If the expression used in an `isa` is a variable, then its static type is automatically adapted, therefore avoiding the need of a specific cast.
78
79 ~~~
80 var x: Object = whatever
81 if x isa Int then
82     # static type of x is Int
83     print x * 10 # OK
84 end
85 ~~~
86
87 Remember that adaptive typing follows the control flow, including the Boolean operators.
88
89 ~~~
90 var a: Array[Object] = ...
91 for i in a do
92     # the static type of i is Object
93     if not i isa Int then continue
94     # now the static type of i is Int
95     print i * 10 # OK
96 end
97 ~~~
98
99 An interesting example:
100
101 ~~~
102 var max = 0
103 for i in whatever do
104     if i isa Int and i > max then max = i
105     # the > is valid since, in the right part
106     # of the "and", the static type of i is Int
107 end
108 ~~~
109
110 Note that type adaptation occurs only in an `isa` if the target type is more specific that the current type.
111
112 ~~~
113 var a: Collection[Int] = ...
114 if a isa Comparable then
115     # the static type is still Collection[Int]
116     # even if the dynamic type of a is a subclass
117     # of both Collection[Int] and Comparable
118     ...
119 end
120 ~~~
121
122 ## Nullable Types
123
124 `null` is a literal value that is only accepted by some specific static types. However, thanks to adaptive typing, the static type management can be mainly automatic.
125
126 `nullable` annotates types that can accept `null` or an expression of a compatible nullable static type.
127
128 ~~~
129 var x: nullable Int
130 var y: Int
131 x = 1 # OK
132 y = 1 # OK
133 x = null # OK
134 y = null # Compile error
135 x = y # OK
136 y = x # Compile error
137 ~~~
138
139 Adaptive typing works well with nullable types.
140
141 ~~~
142 var x
143 if whatever then
144     x = 5
145 else
146     x = null
147 end
148 # The static type of x is nullable Int
149 ~~~
150
151 Moreover, like the `isa` keyword, the `==` and `!=` operators can adapt the static type of a variable when compared to `null`.
152
153 ~~~
154 var x: nullable Int = whatever
155 if x != null then
156     # The static type of x is Int (without nullable)
157     print x + 6
158 end
159 # The static type of x is nullable Int
160 ~~~
161
162 And another example:
163
164 ~~~
165 var x: nullable Int = whatever
166 loop
167     if x == null then continue
168     # The static type of x is Int
169 end
170 ~~~
171
172 `or else` can be used to compose a nullable expression with any other expression. The value of `x or else y` is `x` if `x` is not `null` and is `y` if `x` is null. The static type of `x or else y` is the combination of the type of `y` and the not null version of the type of `x`.
173
174 ~~~
175 var i: nullable Int = ...
176 var j = i or else 0
177 # the static type of j is Int (without nullable)
178 ~~~
179
180 Note that nullable types require a special management for [[attributes|attribute]] and [[constructors|constructor]].
181
182 ## Explicit Cast
183
184 `as` casts an expression to a type. The expression is either casted successfully or there is an `abort`.
185
186 ~~~
187 var x: Object = 5 # static type of x is Object
188 print x.as(Int) * 10 # outputs 50
189 print x.as(String) # aborts: cast failed
190 ~~~
191
192 Note that `as` does not change the object nor does perform conversion.
193
194 ~~~
195 var x: Object = 5 # static type of x is Object
196 print x.as(Int) + 10 # outputs "15"
197 print x.to_s + "10" # outputs "510"
198 ~~~
199
200 Because of type adaptation, `as` is rarely used on variables. `isa` (sometime coupled with `assert`) is preferred.
201
202 ~~~
203 var x: Object = 5 # static type of x is Object
204 assert x isa Int
205 # static type of x is now Int
206 print x * 10 # outputs 50
207 ~~~
208
209 `as(not null)` can be used to cast an expression typed by a nullable type to its non nullable version. This form keeps the programmer from writing explicit static types.
210
211 ~~~
212 var x: nullable Int = 5 # static type of x is nullable Int
213 print x.as(not null) * 10 # cast, outputs 50
214 print x.as(Int) * 10 # same cast, outputs 50
215 assert x != null # same cast, but type of x is now Int
216 print x * 10 # outputs 50
217 ~~~
218
219 ## Static Type Combination Rule
220
221 Adaptive typing, literal arrays, and `or else` need to determine a static type by combining other static types. This is done by using the following rule:
222
223 -   The final type is `nullable` if at least one of the types is `nullable`.
224
225 -   The final type is the static type that is more general than all the other types.
226
227 -   If there is no such a type, and the thing typed is a variable, then the final type is the upper bound type of the variable; else there is a compilation error.
228
229 <!-- -->
230
231 ~~~
232 var d: Discrete = ...
233 # Note: Int < Discrete < Object
234 var x
235 if whatever then x = 1 else x = d
236 # static type is Discrete
237 if whatever then x = 1 else x = "1"
238 # static type is nullable Object (upper bound)
239 var a1 = [1, d] # a1 is a Array[Discrete]
240 var a2 = [1, "1"] # Compile error:
241         # incompatible types Int and String
242 ~~~