Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Now able to generate a single-page, HTML version, of the documentation. #4

Merged
merged 24 commits into from over 2 years ago

2 participants

Romain Ruetschi Amos Wenger
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
2  .gitignore
... ... @@ -1,4 +1,6 @@
1 1 .libs
  2 +_build/
2 3 *.swp
3 4 *.swo
4 5 *_tmp/
  6 +.DS_Store
5 000-intro.md
Source Rendered
... ... @@ -0,0 +1,5 @@
  1 +
  2 +Introduction
  3 +============
  4 +
  5 +*Coming soon, hopefully.*
45 constructors.md → 001-constructors.md
Source Rendered
... ... @@ -1,18 +1,19 @@
  1 +
1 2 Constructors
2 3 ============
3 4
4   -Intro
5   ------
6   -
7 5 In ooc, unlike Java/Scala/C++/C#, 'new' isn't a keyword, but a static method.
8 6
9 7 For example:
10 8
  9 +~~~
11 10 dog := Dog new("Pif")
  11 +~~~
12 12
13 13 However it's uncommon to directly define a new method. Instead, an init method is
14 14 defined, like this:
15 15
  16 +~~~
16 17 Dog: class {
17 18
18 19 name: String
@@ -20,10 +21,12 @@ defined, like this:
20 21 init: func (=name) {}
21 22
22 23 }
  24 +~~~
23 25
24 26 When an 'init' method is defined, a corresponding 'new' static method is defined, in our case,
25 27 the code above is equivalent to:
26 28
  29 +~~~
27 30 Dog: class {
28 31
29 32 name: String
@@ -39,9 +42,11 @@ the code above is equivalent to:
39 42 }
40 43
41 44 }
  45 +~~~
42 46
43 47 'alloc' is a method of Class, which can be defined like this, for example:
44 48
  49 +~~~
45 50 /// Create a new instance of the object of type defined by this class
46 51 alloc: final func ~_class -> Object {
47 52 object := gc_malloc(instanceSize) as Object
@@ -50,6 +55,7 @@ the code above is equivalent to:
50 55 }
51 56 return object
52 57 }
  58 +~~~
53 59
54 60 In ooc implementations, Object and Class are often classes defined in .ooc source
55 61 files, so you can easily study their source code. You can typically find their definitions
@@ -60,14 +66,17 @@ Reminder: member-arguments and assign-arguments
60 66
61 67 This:
62 68
  69 +~~~
63 70 DiceRoll: class {
64 71 value: Int
65 72
66 73 init: func (=value) {}
67 74 }
  75 +~~~
68 76
69 77 is the equivalent of this:
70 78
  79 +~~~
71 80 DiceRoll: class {
72 81 value: Int
73 82
@@ -75,9 +84,11 @@ is the equivalent of this:
75 84 this value = value
76 85 }
77 86 }
  87 +~~~
78 88
79 89 which is the equivalent of this:
80 90
  91 +~~~
81 92 DiceRoll: class {
82 93 value: Int
83 94
@@ -85,6 +96,7 @@ which is the equivalent of this:
85 96 this value = value
86 97 }
87 98 }
  99 +~~~
88 100
89 101 Ie '.' allows 'value's type to be inferred from the member variable
90 102 of the same name, and '=' does the same plus assigns it in the constructor.
@@ -107,6 +119,7 @@ method.
107 119
108 120 You can also call a super-constructor with super()
109 121
  122 +~~~
110 123 Dog: class {
111 124
112 125 name: String
@@ -118,6 +131,7 @@ You can also call a super-constructor with super()
118 131 init: func (=name) {}
119 132
120 133 }
  134 +~~~
121 135
122 136 Inheritance
123 137 -----------
@@ -125,6 +139,7 @@ Inheritance
125 139 A common mistake is to think that constructor are inherited, because they are standard
126 140 methods. However, this behavior would be harmful, as explained in the following example:
127 141
  142 +~~~
128 143 Logger: class {
129 144 prefix: String
130 145
@@ -147,12 +162,15 @@ methods. However, this behavior would be harmful, as explained in the following
147 162 output write(prefix). write(msg). write('\n')
148 163 }
149 164 }
  165 +~~~
150 166
151 167 What would happen if the first constructor defined in Logger was available
152 168 for FileLogger? Let's find out
153 169
  170 +~~~
154 171 warn := FileLogger new("WARN")
155 172 warn log("Somebody set us up the stacktrace")
  173 +~~~
156 174
157 175 The constructor call, if it was valid, would either return a Logger, which is
158 176 not what we want, or by some miracle trick, return a FileLogger - but one
@@ -164,6 +182,7 @@ Super func (and beyond)
164 182 However, there are times when one truly wants to relay a constructor
165 183 in an inherited class, such as:
166 184
  185 +~~~
167 186 Expression: abstract class {
168 187 eval: abstract func -> Int
169 188 }
@@ -177,6 +196,7 @@ in an inherited class, such as:
177 196 Add: class extends BinaryOp {
178 197 init: func ~lr (=left, =right) {}
179 198 }
  199 +~~~
180 200
181 201 Repeating the 'init~lr' definition in Add violates the Don't Repeat Yourself (DRI)
182 202 principle. Besides, if functionality is added to the base BinaryOp init~lr, it
@@ -184,29 +204,18 @@ wouldn't be replicated in Add init~lr.
184 204
185 205 For this precise case, the 'super func' construct exists:
186 206
  207 +~~~
187 208 Add: class extends BinaryOp {
188 209 init: super func ~lr
189 210 }
  211 +~~~
190 212
191 213 This behaves exactly as if we had written:
192 214
  215 +~~~
193 216 Add: class extends BinaryOp {
194 217 init: func ~lr (.left, .right) {
195 218 super(left, right)
196 219 }
197 220 }
198   -
199   -
200   -
201   -
202   -
203   -
204   -
205   -
206   -
207   -
208   -
209   -
210   -
211   -
212   -
  221 +~~~
22 covers-vs-classes.md → 002-covers-vs-classes.md
Source Rendered
... ... @@ -1,3 +1,4 @@
  1 +
1 2 When to use covers and classes
2 3 ==============================
3 4
@@ -17,6 +18,7 @@ By-reference, by-value
17 18
18 19 Classes are by-references. Which means every object is a reference. Doing that:
19 20
  21 +~~~
20 22 Number: class {
21 23 value: Int
22 24 init: func (=value) {}
@@ -33,6 +35,7 @@ Classes are by-references. Which means every object is a reference. Doing that:
33 35 answer := Number new(42)
34 36 modify(answer) // does nothing
35 37 modifyInside(answer)
  38 +~~~
36 39
37 40 What happens in 'modifyRef' is that we change what 'n' refers to in the
38 41 modifyRef function. It doesn't modify what 'n' referred to in the first place,
@@ -50,24 +53,29 @@ Covers are trickier. There are two types of covers: primitive covers, and compou
50 53 Primitive covers allow to add methods to an existing type. For implementations
51 54 of ooc on top of C, it means you can do stuff like:
52 55
  56 +~~~
53 57 Int: cover from int
  58 +~~~
54 59
55 60 And it's actually the way all C types are used from ooc.
56 61
57 62 As a consequence, covers are by-value. Which means that
58 63
  64 +~~~
59 65 modify: func (i: Int) {
60 66 i = -1
61 67 }
62 68
63 69 answer := 42
64 70 modify(answer)
  71 +~~~
65 72
66 73 Doesn't modify answer.
67 74
68 75 But compound covers (you can think of them as structs) are also by value,
69 76 which means that:
70 77
  78 +~~~
71 79 Number: cover {
72 80 value: Int
73 81 }
@@ -80,6 +88,7 @@ which means that:
80 88 answer value = 42
81 89
82 90 modifyInside(answer)
  91 +~~~
83 92
84 93 Won't modify 'answer' at all, but a *copy* of it that has been
85 94 passed to 'modifyInside'.
@@ -88,18 +97,22 @@ As an interesting side effect, a 'clone' method is futile for covers.
88 97
89 98 It also means that this won't work:
90 99
  100 +~~~
91 101 Number: cover {
92 102 value: Int
93 103 init: func (=value) {}
94 104 }
  105 +~~~
95 106
96 107 Because init will be working on a *copy* of the object, thus leaving
97 108 the original object unmodified. That's why func@ exists, ie.:
98 109
  110 +~~~
99 111 Number: cover {
100 112 value: Int
101 113 init: func@ (=value) {}
102 114 }
  115 +~~~
103 116
104 117 Where 'this' will be passed by reference. Same goes for any cover method
105 118 that modifies its content.
@@ -109,15 +122,19 @@ Heap allocation, stack allocation
109 122
110 123 When you do
111 124
  125 +~~~
112 126 NumberClass: class {}
113 127 n := NumberClass new()
  128 +~~~
114 129
115 130 n may be allocated on the heap or on the stack, however the compiler sees fit.
116 131
117 132 However, with:
118 133
  134 +~~~
119 135 NumberCover: cover {}
120 136 n: NumberCover
  137 +~~~
121 138
122 139 n is allocated on the stack.
123 140
@@ -174,6 +191,7 @@ Stack-allocated variables are deallocated when they go out of scope.
174 191
175 192 What does that mean? It means that this code is wrong.
176 193
  194 +~~~
177 195 getAnswer: func -> Int* {
178 196 // answer is allocated on the stack
179 197 answer := 42
@@ -186,9 +204,11 @@ What does that mean? It means that this code is wrong.
186 204
187 205 answerPtr := getAnswer()
188 206 "answer = %d" printfln(answerPtr@)
  207 +~~~
189 208
190 209 Whereas this one will work perfectly:
191 210
  211 +~~~
192 212 getAnswer: func -> Int* {
193 213 // answer is allocated on the heap
194 214 answer := gc_malloc(Int size)
@@ -202,6 +222,7 @@ Whereas this one will work perfectly:
202 222
203 223 answerPtr := getAnswer()
204 224 "answer = %d" printfln(answerPtr@)
  225 +~~~
205 226
206 227 However, the first version (returning the address of a local variable)
207 228 might work sometimes: don't be surprised. If the memory address (on the stack
@@ -221,4 +242,3 @@ However, keep in mind that allocation is often not the first place to look
221 242 if you want to optimize your application. Remember to always use a profiler
222 243 (I find that valgrind + KCachegrind work particularly well with ooc code)
223 244 to figure out where the hotspots are in your code.
224   -
50 first-class-functions.md → 003-first-class-functions.md
Source Rendered
... ... @@ -1,3 +1,4 @@
  1 +
1 2 First-class functions
2 3 =====================
3 4
@@ -8,9 +9,11 @@ Functions are pieces of code that can take arguments, and return values.
8 9
9 10 Named functions are declared with this syntax:
10 11
  12 +~~~
11 13 <name> : func <arguments> <return type> {
12 14 <body>
13 15 }
  16 +~~~
14 17
15 18 Where arguments are comma-separated, enclosed between parenthesis, and return type
16 19 is prefixed with a right arrow ->.
@@ -20,23 +23,29 @@ may be omitted too, if the function is void.
20 23
21 24 Example:
22 25
  26 +~~~
23 27 max: func (a, b: Int) -> Int {
24 28 a > b ? a : b
25 29 }
  30 +~~~
26 31
27 32 But this is a valid expression too:
28 33
  34 +~~~
29 35 func <arguments> <return type> {
30 36 <body>
31 37 }
  38 +~~~
32 39
33 40 And with decl-assign, we can declare a variable named 'max', equal
34 41 to this expression. And then use it very much like a function
35 42
  43 +~~~
36 44 max := func (a, b: Int) -> Int {
37 45 a > b ? a : b
38 46 }
39 47 answer := max(-1, 42)
  48 +~~~
40 49
41 50 Differences between function and first-class functions
42 51 ------------------------------------------------------
@@ -44,6 +53,7 @@ Differences between function and first-class functions
44 53 The first difference is: functions are immutable. First-class functions
45 54 are variables, and thus can be overwritten by simple assignment.
46 55
  56 +~~~
47 57 // this is invalid: don't do that.
48 58 someFunc: func {}
49 59 someFunc = someOtherFunc
@@ -51,10 +61,12 @@ are variables, and thus can be overwritten by simple assignment.
51 61 // this, on the other hand, is valid
52 62 someFunc := func {}
53 63 someFunc = someOtherFunc
  64 +~~~
54 65
55 66 The second difference is: first-class functions can capture context.
56 67 Closures are first-class functions that capture context.
57 68
  69 +~~~
58 70 // here's a normal function
59 71 clone: func (l: List<Int>) -> List<Int> {
60 72 copy := ArrayList<Int> new(l size())
@@ -63,12 +75,15 @@ Closures are first-class functions that capture context.
63 75 })
64 76 copy
65 77 }
  78 +~~~
66 79
67 80 Here, our anonymous, first-class function which also happens to be a closure, is
68 81
  82 +~~~
69 83 func(element: Int) {
70 84 copy add(element)
71 85 }
  86 +~~~
72 87
73 88 It captures the context because we access 'copy' in it - which isn't an
74 89 argument of the function, nor a variable declared inside the function.
@@ -84,22 +99,28 @@ The type of first-class functions
84 99
85 100 So, when we do:
86 101
  102 +~~~
87 103 max := func (a, b: Int) -> Int {
88 104 a > b ? a : b
89 105 }
  106 +~~~
90 107
91 108 What exactly is the type of 'max' ?
92 109
93 110 Let's declare it in two steps instead:
94 111
  112 +~~~
95 113 max : Func (Int, Int) -> Int
96 114 max = func (a, b: Int) -> Int {
97 115 a > b ? a : b
98 116 }
  117 +~~~
99 118
100 119 `Func` is a type that has a special syntax:
101 120
  121 +~~~
102 122 Func <argument types> <return type>
  123 +~~~
103 124
104 125 As with regular functions declaration, both argument types and return types
105 126 can be omitted.
@@ -111,52 +132,65 @@ Declaring the type of first-class functions is mostly useful in function argumen
111 132
112 133 For example, in the SDK, the declaration of each goes like this:
113 134
  135 +~~~
114 136 List: class <T> {
115 137 each: func(f: Func (T)) {
116 138 // ...
117 139 }
118 140 }
  141 +~~~
119 142
120 143 So it takes a function that takes one argument of type T
121 144
122 145 Hence, clearly doing that in our clone function above:
123 146
  147 +~~~
124 148 l each(func(element: Int) {
125 149 copy add(element)
126 150 })
  151 +~~~
127 152
128 153 Is unnecessary. Since we know that l is a List<Int>, and that each takes
129 154 a Func (T) then we know that element is of type Int.
130 155
131 156 And thus, we can write that:
132 157
  158 +~~~
133 159 l each(|element|
134 160 copy add(elements)
135 161 )
  162 +~~~
136 163
137 164 The proper syntax for that is
138 165
  166 +~~~
139 167 call(|<name of arguments>|
140 168 <body>
141 169 )
  170 +~~~
142 171
143 172 If there are no arguments, this is valid:
144 173
  174 +~~~
145 175 call(||
146 176 <body>
147 177 )
  178 +~~~
148 179
149 180 And is then equivalent to:
150 181
  182 +~~~
151 183 call(func {
152 184 <body>
153 185 })
  186 +~~~
154 187
155 188 The return type is inferred as well.
156 189
157 190 Other differences - member functions vs member first-class functions
158 191 --------------------------------------------------------------------
159 192
  193 +~~~
160 194 Dog: class {
161 195
162 196 shout: func {
@@ -174,18 +208,22 @@ Other differences - member functions vs member first-class functions
174 208 d2 := Dog new()
175 209 d shout()
176 210 d2 shout()
  211 +~~~
177 212
178 213 Prints:
179 214
  215 +~~~
180 216 Woof woof
181 217 Ruff ruff
182 218 Ruff ruff
  219 +~~~
183 220
184 221 When assigning 'Dog shout', we change the member method of *all* past and
185 222 future Dog instances. This happens because 'shout' is actually stored in the meta-class
186 223
187 224 Consider the differences with that instead:
188 225
  226 +~~~
189 227 Dog: class {
190 228
191 229 shout := func {
@@ -203,21 +241,15 @@ Consider the differences with that instead:
203 241 d2 := Dog new()
204 242 d shout()
205 243 d2 shout()
  244 +~~~
206 245
207 246 Prints:
208 247
  248 +~~~
209 249 Woof woof
210 250 Ruff ruff
211 251 Woof woof
  252 +~~~
212 253
213 254 Here, 'shout' is a member variable. Assigning to 'd shout' changes it
214 255 only for that instance, so d2 shout isn't changed.
215   -
216   -
217   -
218   -
219   -
220   -
221   -
222   -
223   -
96 generics.md → 004-generics.md
Source Rendered
... ... @@ -1,3 +1,4 @@
  1 +
1 2 Generics
2 3 ========
3 4
@@ -9,6 +10,7 @@ Generics are one of the most commonly misunderstood features of ooc.
9 10 Many people attempt confuse them with templates (like in C++ or D) and are
10 11 surprised when things like this don't work:
11 12
  13 +~~~
12 14 Vector2: class <T> {
13 15 x, y: T
14 16 init: func(=x, =y) {}
@@ -16,6 +18,7 @@ surprised when things like this don't work:
16 18 new(x + r x, y + r y)
17 19 }
18 20 }
  21 +~~~
19 22
20 23 (Don't worry about the syntax for now, I'll get to it later)
21 24
@@ -39,13 +42,17 @@ methods on them, then what are they good for? Sure looks useless from here.
39 42
40 43 Well, here's one thing we can do, for example:
41 44
  45 +~~~
42 46 identity: func <T> (val: T) -> T {
43 47 val
44 48 }
  49 +~~~
45 50
46 51 Woha. What just happened here? Let's recap line by line.
47 52
  53 +~~~
48 54 identity: func <T> (val: T) -> T
  55 +~~~
49 56
50 57 Here, we declare a function named 'identity', with one type parameter named T,
51 58 taking one parameter named 'val', and returning a value of type T.
@@ -58,20 +65,26 @@ When you declare a type parameter, it tells the compiler about a new type,
58 65 that we know nothing about at compile-time. Well, not nothing. Remember
59 66 classes? Here's how we access the class of an object:
60 67
  68 +~~~
61 69 object class
  70 +~~~
62 71
63 72 And if object was of type Carrot, that amounts exactly to doing just:
64 73
  74 +~~~
65 75 Carrot
  76 +~~~
66 77
67 78 What is that, exactly? It's an access to a class. What is a class? An instance
68 79 of Class, which is declared in lang/CoreTypes.ooc If you actually go on and open
69 80 CoreTypes, here is a simplified version of what you will find:
70 81
  82 +~~~
71 83 Class: class {
72   - name: String
73   - size, instanceSize: SizeT
  84 + name: String
  85 + size, instanceSize: SizeT
74 86 }
  87 +~~~
75 88
76 89 (Reminder: SizeT can be used to store the size of something. On 32-bits
77 90 platforms, it's 32-bits wide. On 64-bits platforms, it's 64-bits wide, and so
@@ -81,8 +94,10 @@ So back to our generic stuff. I said we knew nothing about generic types. And
81 94 in fact, it was a downright lie. Please accept my apologies. The reality is -
82 95 we know all that matters! If you try to execute the following piece of code:
83 96
  97 +~~~
84 98 test: func <T> (t: T) { T class name println() }
85 99 test(42)
  100 +~~~
86 101
87 102 You'll find out something very strange and puzzling.. it prints "Class" !
88 103
@@ -90,11 +105,13 @@ We just discovered that we can access type parameters just like any other
90 105 variable. And since T is a class, and we can access various fields of a class,
91 106 here's what we can do:
92 107
  108 +~~~
93 109 test2: func <T> (t: T) {
94   - "name = %s, size = %zd, instanceSize = %zd" printfln(
95   - T name, T size, T instanceSize)
  110 + "name = %s, size = %zd, instanceSize = %zd" printfln(
  111 + T name, T size, T instanceSize)
96 112 }
97 113 test2(42)
  114 +~~~
98 115
99 116 This will likely print something like "name = Int, size = 4, instanceSize =
100 117 4".
@@ -109,7 +126,9 @@ But I digress. (Then again, you're the curious one - not me.)
109 126
110 127 So let's analyze the second line of our 'identity' function above:
111 128
  129 +~~~
112 130 val
  131 +~~~
113 132
114 133 Let's see. It's the last line of a non-void function, so it means it's
115 134 returned. 'val' refers to a variable declaration which happens to be a
@@ -119,9 +138,11 @@ last line to yourself two or three times to impreign it into your brain)
119 138 So basically what our function does is... just pass through what we give it as
120 139 an argument! Let's try that
121 140
  141 +~~~
122 142 42 toString() println() // just to be sure
123 143 identity(42) toString() println() // still a little trivial
124 144 identity(identity(identity(identity(42)))) toString() println() // whoa.
  145 +~~~
125 146
126 147 Yup, it prints 42 alright.
127 148
@@ -138,9 +159,11 @@ Generic type inference
138 159
139 160 Let's do a little experiment:
140 161
  162 +~~~
141 163 a := 42
142 164 b := identity(42)
143 165 "%s and %s" printfln(a class name, b class name)
  166 +~~~
144 167
145 168 What did you get? Int and Int, right? But - but the return type of 'identity'
146 169 is T! Shouldn't b's type be T too?
@@ -156,11 +179,15 @@ all.
156 179
157 180 You see, when you call:
158 181
  182 +~~~
159 183 identity(42)
  184 +~~~
160 185
161 186 And the definition of identity is
162 187
  188 +~~~
163 189 identity: func <T> (val: T) -> T
  190 +~~~
164 191
165 192 Here's what the compiler figures out: well, we have one unknown type (that
166 193 is, generic type), called 'T'. Also, the first (and only) argument is of that
@@ -172,8 +199,10 @@ avoid tons of cast, and is good for your karma.
172 199
173 200 Here's another example.
174 201
  202 +~~~
175 203 printTypeName: func <T> (T: Class) { T name println() }
176 204 printTypeName(Object)
  205 +~~~
177 206
178 207 Then it prints "Object". Did we find a way to print strings without having to
179 208 enclose them between quotes? Hopefully not. That would be messy, man. Talk
@@ -188,8 +217,10 @@ T'. It is then not too big a challenge for the compiler to go from here.
188 217
189 218 Then again, we could have done:
190 219
  220 +~~~
191 221 dumbPrintTypeName: func (T: Class) { T name println() }
192 222 dumbPrintTypeName(Object)
  223 +~~~
193 224
194 225 Since we don't use T as a type anywhere. So why even bother with this \<T\>
195 226 thing, hmm? Why does the compiler even allow it? Read on if you want to find out.
@@ -200,8 +231,10 @@ Generic return types
200 231 Here's a little riddle for you. How does the compiler figure out the real return
201 232 type of this function:
202 233
  234 +~~~
203 235 sackOfUnknown: func <T> -> T { 42 }
204 236 sackOfUnknown()
  237 +~~~
205 238
206 239 Anyone? Ah, I see a hand in the back. What do you say? The type of the return
207 240 expression? WRONG. But that was an honest try. One point for effort.
@@ -221,18 +254,20 @@ So how do we make a function that
221 254 Well, that's precisely where that useless thing presented in the previous
222 255 section comes in very handy:
223 256
224   - theAnswer: func <T> (T: Class) -> T {
225   - match T {
226   - case Int => 42
227   - case Float => 42.0
228   - case String => "forty-two"
229   - case => Exception new("You're not worthy.") throw(); 0
230   - }
231   - }
  257 +~~~
  258 + theAnswer: func <T> (T: Class) -> T {
  259 + match T {
  260 + case Int => 42
  261 + case Float => 42.0
  262 + case String => "forty-two"
  263 + case => Exception new("You're not worthy.") throw(); 0
  264 + }
  265 + }
232 266 rational := theAnswer(Int)
233 267 real := theAnswer(Float)
234 268 text := theAnswer(String)
235 269 theAnswer(Object) // ka-boom!
  270 +~~~
236 271
237 272 What just happened? We used a match on 'T', which means we're comparing it.
238 273 We're comparing it with the types 'Int', 'Float', 'String', trying to return
@@ -249,23 +284,27 @@ do? Well - store them! That's the way all collections work.
249 284
250 285 Let's start with a simple one:
251 286
  287 +~~~
252 288 Slot: class <T> {
253 289 element: T
254   - init: func (.element) { set(element) }
255   - set: func (=element) {}
256   - get: func -> T { element }
  290 + init: func (.element) { set(element) }
  291 + set: func (=element) {}
  292 + get: func -> T { element }
257 293 }
258 294
259 295 s := Slot new(3.14)
260 296 s get() toString() println()
261 297 s T name println()
  298 +~~~
262 299
263 300 Not that bad, eh? (It should print 3.14 and Float - or some other type, if
264 301 you're in the future and ooc has a proper number tower)
265 302
266 303 But wait - get is defined like that:
267 304
  305 +~~~
268 306 get: func -> T { element }
  307 +~~~
269 308
270 309 And clearly T is a generic type, ie. it could be anything at runtime, and
271 310 *yet* the compiler figures it out right.
@@ -273,23 +312,31 @@ And clearly T is a generic type, ie. it could be anything at runtime, and
273 312 So what happens here? Let's look at the call, since it's the info from which
274 313 the compiler works to infer generic types:
275 314
  315 +~~~
276 316 s get()
  317 +~~~
277 318
278 319 Hmmph. Not many types there - except maybe.. the type of s. Which is what
279 320 exactly?
280 321
  322 +~~~
281 323 s := Slot new(3.14)
  324 +~~~
282 325
283 326 Well it turns out that Slot new is just a regular method call, the generic
284 327 type T is inferred to 'Float', and so 's' becomes a Slot\<Float\>
285 328
286 329 Hence, the compiler sees the get() call as:
287 330
  331 +~~~
288 332 Slot<Float> get()
  333 +~~~
289 334
290 335 And it sees the get definition as
291 336
  337 +~~~
292 338 Sloat<T> get: func {}
  339 +~~~
293 340
294 341 From here, inferring that T = Float is trivial.
295 342
@@ -300,7 +347,9 @@ One of the most advanced example of type inference in the whole SDK
300 347 is probably the List map() function. Here is its signature (ie.
301 348 definition without the body) :
302 349
  350 +~~~
303 351 map: func <K> (f: Func (T) -> K) -> This<K>
  352 +~~~
304 353
305 354 So basically it turns a List\<T\> into a List\<K\>, by calling f to turn
306 355 every T into a K. Makes sense.
@@ -311,14 +360,18 @@ to the function.
311 360
312 361 Well - no big deal then, if we do:
313 362
  363 +~~~
314 364 intToString: func (i: Int) -> String { i toString() }
315 365 strings := numbers map(intToString)
  366 +~~~
316 367
317 368 Then we know that K = String from the definition of intToString.
318 369
319 370 But wait, there's a nice infers-everything syntax for closures, ie.:
320 371
  372 +~~~
321 373 stringsToo := numbers map(|x| x toString())
  374 +~~~
322 375
323 376 And here, we're doomed. The closure insides attempts to infers its whole
324 377 signature (argument types, return type, etc.) from the type of the
@@ -339,28 +392,31 @@ How does it work under the hood?
339 392 Here is the naive implementation: generic type arguments as passed
340 393 as function arguments, ie a call to:
341 394
  395 +~~~
342 396 ArrayList<Int> new()
343 397 identity(42)
  398 +~~~
344 399
345 400 becomes (without mangling):
346 401
  402 +~~~
347 403 ArrayList_new(Int_class());
348 404 identity()
349   -
  405 +~~~
350 406
351 407 Type arguments in classes become variables:
352 408
  409 +~~~
353 410 ArrayList: class <T> {}
  411 +~~~
354 412
355 413 is
356 414
  415 +~~~
357 416 ArrayList: class {
358 417 T: Class
359 418 }
  419 +~~~
360 420
361 421 Class type arguments are assigned in the constructor to the appropriate
362 422 values.
363   -
364   -
365   -
366   -
26 properties.md → 005-properties.md
Source Rendered
... ... @@ -1,3 +1,4 @@
  1 +
1 2 Properties
2 3 ==========
3 4
@@ -14,13 +15,17 @@ and/or set (besides the actual memory read/write).
14 15
15 16 However, this results in long-winded and hard-on-the-eyes code such as this:
16 17
  18 +~~~
17 19 setX(getX() + 1)
18 20 setY(getY() + 2)
19 21 setZ(getZ() + 3)
  22 +~~~
20 23
21 24 When one come simply write, with regular variables
22 25
  26 +~~~
23 27 (x, y, z) += (1, 2, 3)
  28 +~~~
24 29
25 30 Which is much easier on the eyes.
26 31 (Read on 'tuples' for more information about multi-declaration / multi-assignment)
@@ -31,9 +36,11 @@ A dumb property
31 36 Turning a regular variable declaration into a property is as simple
32 37 as adding a pair of brackets {} after it.
33 38
  39 +~~~
34 40 Tree: class {
35 41 age: Int {}
36 42 }
  43 +~~~
37 44
38 45 At this point, 'age' behaves exactly as a variable, except that instead
39 46 of direct memory read/write, it's now modified via automatically-generated
@@ -41,18 +48,22 @@ getters and setters.
41 48
42 49 The above code is also equivalent to:
43 50
  51 +~~~
44 52 Tree: class {
45 53 age: Int { get set }
46 54 }
  55 +~~~
47 56
48 57 Or, if you prefer:
49 58
  59 +~~~
50 60 Tree: class {
51 61 age: Int {
52 62 get
53 63 set
54 64 }
55 65 }
  66 +~~~
56 67
57 68 Hooking on get and set
58 69 ----------------------
@@ -60,6 +71,7 @@ Hooking on get and set
60 71 There's more to it. get and set can have a body, much like methods, except
61 72 without specifying argument types or return types.
62 73
  74 +~~~
63 75 Tree: class {
64 76 age: Int {
65 77 get
@@ -68,6 +80,7 @@ without specifying argument types or return types.
68 80 }
69 81 }
70 82 }
  83 +~~~
71 84
72 85 In this example, validation is done within the property setter.
73 86 It could be used to validate state transitions for a finite state machine,
@@ -83,11 +96,13 @@ variable of the same name existing.
83 96 For our tree class, we might define an 'old' property that is computed from
84 97 its 'age' property.
85 98
  99 +~~~
86 100 old: Bool {
87 101 get {
88 102 age > 100
89 103 }
90 104 }
  105 +~~~
91 106
92 107 NOTE: using undocumented 'magic numbers' in code is bad practice: don't do it.
93 108 Use constants with meaningful names instead - or better yet, make it configurable.
@@ -108,6 +123,7 @@ Foreign function interfacing
108 123 Properties setters and getters can be extern functions (ie. functions defined
109 124 outside ooc code). Let's take an example for a well-known GTK widget:
110 125
  126 +~~~
111 127 use gtk
112 128 import gtk/[Gtk, Widget]
113 129
@@ -119,15 +135,7 @@ outside ooc code). Let's take an example for a well-known GTK widget:
119 135 get: extern(gtk_label_get_text)
120 136 }
121 137 }
  138 +~~~
122 139
123 140 Once again, properties make code more readable and more straight-forward
124 141 to write.
125   -
126   -
127   -
128   -
129   -
130   -
131   -
132   -
133   -
46 tuples.md → 006-tuples.md
Source Rendered
... ... @@ -1,3 +1,4 @@
  1 +
1 2 Tuples
2 3 ======
3 4
@@ -22,6 +23,7 @@ How do we make a function that return several values?
22 23
23 24 You can use an array:
24 25
  26 +~~~
25 27 // counter-example: don't do that
26 28 minmax: func (list: List<Int>) -> Int[] {
27 29 min := INT_MAX
@@ -33,13 +35,16 @@ You can use an array:
33 35
34 36 [min, max]
35 37 }
  38 +~~~
36 39
37 40 But it's not practical, ie. if you want to retrieve min and max, you have to do:
38 41
  42 +~~~
39 43 // counter-example: don't do that
40 44 result := minmax(mylist)
41 45 min := result[0]
42 46 max := result[1]
  47 +~~~
43 48
44 49 We're using three lines only to retrieve results from a function.
45 50
@@ -52,6 +57,7 @@ Using an array doesn't allow different types, so
52 57
53 58 Let's try using a list of cells:
54 59
  60 +~~~
55 61 // counter-example: don't do that
56 62 meanAndTotal: func (units: List<Unit>) -> List<Cell> {
57 63 total := 0
@@ -60,13 +66,16 @@ Let's try using a list of cells:
60 66
61 67 [Cell new(total), Cell new(mean)] as ArrayList<Cell>
62 68 }
  69 +~~~
63 70
64 71 And to retrieve the values:
65 72
  73 +~~~
66 74 // counter-example: don't do that
67 75 result := meanAndTotal(units)
68 76 total := result[0] get(Int)
69 77 mean := result[1] get(Float)
  78 +~~~
70 79
71 80 Again, three lines, looks even uglier, no guarantees, not type-safe at
72 81 compile-time. Don't do that.
@@ -76,6 +85,7 @@ compile-time. Don't do that.
76 85 And here's the closest we'll come to a tolerable solution without using
77 86 tuples: out-parameters. Let's rewrite the minmax example with it
78 87
  88 +~~~
79 89 // counter-example: don't do that
80 90 minmax: func (list: List<Int>, min, max: Int@) {
81 91 min = INT_MAX
@@ -85,16 +95,21 @@ tuples: out-parameters. Let's rewrite the minmax example with it
85 95 if(i > max) max = i