1 | == CHICKEN for Ruby programmers |
---|
2 | |
---|
3 | [[toc:]] |
---|
4 | |
---|
5 | If you don't know much about CHICKEN yet, please take a moment to read |
---|
6 | the introductory part of [[/manual|The User's Manual]]. You're back? Good! |
---|
7 | |
---|
8 | === Paradigm independence |
---|
9 | |
---|
10 | The most important design feature of Ruby is that it is purely |
---|
11 | object-oriented; everything is an object. Scheme is ''not'' an |
---|
12 | object-oriented language. In fact, it does not commit to any |
---|
13 | particular programming paradigm -- it offers ''complete and total |
---|
14 | freedom to the programmer''. If you decide (a part of) a program is |
---|
15 | best implemented in an object-oriented fashion, you can choose to use |
---|
16 | one of the many object systems. Have a quick glance at the |
---|
17 | [[http://wiki.call-cc.org/chicken-projects/egg-index-4.html#oop|OOP category]] of |
---|
18 | the egg index to get an impression of the |
---|
19 | diversity of styles of object oriented programming you can use with |
---|
20 | CHICKEN. By the way, the list on that page shows all the available |
---|
21 | ''eggs'' for CHICKEN. We'll explain all about these [[#Eggs|later]]. |
---|
22 | |
---|
23 | Besides object-oriented programming, you can also program in a |
---|
24 | procedural fashion (like you would in Pascal, for example) or in a |
---|
25 | functional style (a bit like Haskell or ML) and you can even |
---|
26 | experiment with message-passing like in Erlang, logic programming |
---|
27 | like you would with Prolog, or stack languages like Forth and Factor. |
---|
28 | |
---|
29 | === Origins |
---|
30 | |
---|
31 | Ruby's origins are firmly rooted in Lisp. It takes many things and |
---|
32 | ideas from Lisp (symbols, lambdas, eval, metaprogramming, DSLs etc). |
---|
33 | What it doesn't take from Lisp it takes from Smalltalk, which was |
---|
34 | itself inspired by Lisp's clean syntax. All this means that once |
---|
35 | you're over the initial hump of grokking the syntax, you'll find |
---|
36 | yourself in pretty familiar territory. |
---|
37 | |
---|
38 | Originally Ruby began as an implementation-defined language. That is, |
---|
39 | there was only one implementation (Matz's) and whatever that implementation |
---|
40 | did '''was''' the language spec. Nowadays, Ruby |
---|
41 | has [[http://rubyspec.org/|rubyspec]], an attempt to |
---|
42 | standardize the Ruby language. |
---|
43 | |
---|
44 | Likewise, Scheme is a specification-defined language. There |
---|
45 | is one official language specification which says what Scheme is and |
---|
46 | how it works. CHICKEN is simply an implementation of that |
---|
47 | specification. There is one thing that is important to know right |
---|
48 | now: The Scheme specification is ''extremely'' minimal in design. It |
---|
49 | tries to define as few language constructs as possible, but these few |
---|
50 | should be so powerful that you will not need any more to make |
---|
51 | programs. This results in an extremely small spec and a very elegant |
---|
52 | and clean language with very few rules to remember, which makes it |
---|
53 | easy to learn. |
---|
54 | |
---|
55 | However, in the real world you will need much more than just |
---|
56 | programming constructs, you need things to interact with the operating |
---|
57 | system, networking libraries, etc. That's where the difference |
---|
58 | between Scheme and other languages comes in: Every implementation of |
---|
59 | Scheme defines the things it thinks are necessary to build real |
---|
60 | programs with. Unfortunately, this means most Scheme programs are |
---|
61 | inherently not portable from one implementation to another, but it also means that Scheme implementations |
---|
62 | are free to experiment how they want and explore new language |
---|
63 | territory. This gives each Scheme implementation its uniqueness. |
---|
64 | |
---|
65 | Fortunately, most Scheme implementations including CHICKEN are fairly portable to modern hardware architectures and operating systems. You may or may not be able to port a CHICKEN program to another Scheme, but you can port it from Windows to Mac OS X or Linux, or from the 32-bit Intel 386 to the 64-bit SPARC architecture, because CHICKEN runs in all those environments. Usually "porting" just means "running it on that system", because just like Ruby CHICKEN has procedures that are platform-independent, with multiple implementations where the platform itself differs. |
---|
66 | |
---|
67 | CHICKEN's power is in how it extends the Scheme standard. It has a |
---|
68 | very comfortable interface to C that does not require you to touch a |
---|
69 | single line of C code in order to create bindings to existing C |
---|
70 | libraries, but it also gives you the freedom to embed CHICKEN in C |
---|
71 | code or C in CHICKEN code as you want. It offers a TCP/IP networking |
---|
72 | layer, it has great POSIX interoperability so you can interact with |
---|
73 | the OS. And most importantly: It can compile Scheme code to very |
---|
74 | efficient C code which can itself be compiled to machine code, giving |
---|
75 | you the best of both worlds: a dynamic language which allows you to |
---|
76 | program in the flexible way you are used to with Ruby, but this can be |
---|
77 | compiled for maximum efficiency. |
---|
78 | |
---|
79 | == Syntax |
---|
80 | |
---|
81 | === The basics |
---|
82 | |
---|
83 | The one thing that is most strikingly different between Ruby and |
---|
84 | Scheme is of course the syntax. Ruby has an extremely baroque syntax |
---|
85 | that allows you many freedoms in how you would like to write down |
---|
86 | things. Scheme, on the other hand, has only one way in which to write |
---|
87 | a given expression. Let's start by looking at an example. First we |
---|
88 | start by firing up an {{irb}} session and typing a little program: |
---|
89 | |
---|
90 | irb(main):001:0> # My first program |
---|
91 | irb(main):002:0* [1, 2, 3, 4].map{|x| x + 1} |
---|
92 | => [2, 3, 4, 5] |
---|
93 | irb(main):003:0> |
---|
94 | |
---|
95 | Now, we fire up a {{csi}} (chicken scheme interpreter) session: |
---|
96 | |
---|
97 | #;1> ; My first program |
---|
98 | (map add1 (list 1 2 3 4)) |
---|
99 | (2 3 4 5) |
---|
100 | #;2> |
---|
101 | |
---|
102 | In Scheme, lists are delimited with parentheses with its elements |
---|
103 | separated by spaces. As we can see, everything in Scheme is a list, |
---|
104 | even the expressions that you use to tell it what to do! An |
---|
105 | expression in Scheme is always a list with the operator on its first |
---|
106 | position and the operands following it. Procedures that accept no |
---|
107 | arguments are simply a list with only the procedure in it, for example |
---|
108 | {{(newline)}}, which simply displays a newline character. |
---|
109 | |
---|
110 | This simple rule also means that ''every parenthesis has a meaning''. |
---|
111 | You can't add more parentheses or leave off parentheses like you can |
---|
112 | in most Ruby expressions. Adding extra parentheses simply applies |
---|
113 | the resulting expression as if it were a procedure: |
---|
114 | |
---|
115 | #;2> ((newline)) |
---|
116 | |
---|
117 | Error: call of non-procedure: #<unspecified> |
---|
118 | |
---|
119 | Call history: |
---|
120 | |
---|
121 | <syntax> ((newline)) |
---|
122 | <syntax> (newline) |
---|
123 | <eval> ((newline)) |
---|
124 | <eval> (newline) <-- |
---|
125 | |
---|
126 | If {{(newline)}} returned a procedure, it would be called. But as it |
---|
127 | happens, {{newline}} simply returns an unspecified value which is not |
---|
128 | a procedure and thus can't be applied. We can also use the result of |
---|
129 | calling a procedure in the call to another procedure: |
---|
130 | |
---|
131 | #;3> (add1 2) |
---|
132 | 3 |
---|
133 | #;4> (- 10 (add1 2) 1) |
---|
134 | 6 |
---|
135 | |
---|
136 | We see that arithmetic is a little different from Ruby, as a result of |
---|
137 | the simple rules Scheme has for its syntax. This may be a little |
---|
138 | awkward, especially with complex calculations: |
---|
139 | |
---|
140 | #;5> (* (+ 1 (- 6 2) 2) (- 10 5)) |
---|
141 | 35 |
---|
142 | |
---|
143 | In Ruby (and other languages with algebraic syntax) this would have |
---|
144 | been |
---|
145 | |
---|
146 | irb(main):002:0> (1 + (6 - 2) + 2) * (10 - 5) |
---|
147 | => 35 |
---|
148 | irb(main):003:0> # Alternatively: |
---|
149 | irb(main):004:0* (1 + 6 - 2 + 2) * (10 - 5) |
---|
150 | irb(main):005:0> # or even: |
---|
151 | irb(main):006:0* (((1) + ((((6 - 2)))) + 2) * (((10) - ((5))))) |
---|
152 | => 35 |
---|
153 | |
---|
154 | Both types of syntax have their advantages and disadvantages: The |
---|
155 | Ruby-like syntax is more natural, but you have to think about operator |
---|
156 | precedence rules. CHICKEN does not need operator precedence rules |
---|
157 | because the precedence can be determined from the way it's nested, but |
---|
158 | it's less natural for most people (though you get used to it very |
---|
159 | quickly). |
---|
160 | |
---|
161 | Actually, right now you know almost all there is to know |
---|
162 | about Scheme's syntax! CHICKEN has a couple of extensions to the |
---|
163 | basic Scheme syntax, but we won't go into detail here. Later you'll |
---|
164 | see a couple of handy shortcuts, but this is basically it. |
---|
165 | |
---|
166 | === Variables |
---|
167 | |
---|
168 | Variables are names for things. CHICKEN has vary lax rules for |
---|
169 | naming variables. Actually, ''any string'' is a valid identifier |
---|
170 | as long as you quote it correctly. |
---|
171 | |
---|
172 | Ruby: |
---|
173 | |
---|
174 | #;1> x = 10 |
---|
175 | => 10 |
---|
176 | #;2> x |
---|
177 | => 10 |
---|
178 | #;3> x-y-z = 10 |
---|
179 | NameError: undefined local variable or method `x' for main:Object |
---|
180 | from (irb):2 |
---|
181 | |
---|
182 | Scheme: |
---|
183 | |
---|
184 | #;1> (define x 10) |
---|
185 | #;2> x |
---|
186 | 10 |
---|
187 | #;3> (define x-y-z 10) |
---|
188 | #;4> x-y-z |
---|
189 | 10 |
---|
190 | #;5> (define %x% 1) |
---|
191 | #;6> %x% |
---|
192 | 1 |
---|
193 | #;7> (define |a b c| 5) |
---|
194 | #;8> |a b c| |
---|
195 | 5 |
---|
196 | |
---|
197 | As you can see, because of Scheme's operator rules, symbols that would |
---|
198 | normally be separate tokens designating operators have no special |
---|
199 | meaning so we can use it in the middle of a name. The convention in |
---|
200 | Scheme is to use the minus sign as word separator (in Ruby, you would |
---|
201 | use an underscore for separating words). The final example shows how |
---|
202 | any string is allowed as a variable name: if the string contains |
---|
203 | syntax that would mean something else to Scheme you can enclose the |
---|
204 | variable name in pipe symbols. The pipe symbol itself can be escaped |
---|
205 | with a backslash, if you need it to be part of a variable. |
---|
206 | |
---|
207 | To assign to a pre-existing variable we can also use {{set!}}: |
---|
208 | |
---|
209 | #;1> (define x 10) |
---|
210 | #;2> x |
---|
211 | 10 |
---|
212 | #;3> (set! x 20) |
---|
213 | #;4> x |
---|
214 | 20 |
---|
215 | |
---|
216 | Top-level variables can also be overwritten by simply redefining them, |
---|
217 | but in some cases you need {{set!}}. However, set! is a typical |
---|
218 | artifact of an imperative programming style and in clean code you |
---|
219 | want to avoid using it. |
---|
220 | |
---|
221 | Scheme also allows the ''binding'' of local variables. Bound variables |
---|
222 | behave like the defined variables above, however, they are only valid |
---|
223 | within a local scope. The top-level variables we've seen are the equivalent |
---|
224 | of a global in Ruby. |
---|
225 | |
---|
226 | The most common binding constructs are {{let}} and {{let*}}. |
---|
227 | {{let}} allows for any number of bindings, none of which are related. |
---|
228 | |
---|
229 | <enscript highlight=scheme> |
---|
230 | (let ((a 5) |
---|
231 | (b 10)) |
---|
232 | (+ a b)) |
---|
233 | </enscript> |
---|
234 | |
---|
235 | {{let*}} is like let, except that the bindings are evaluated in order, |
---|
236 | so subsequent bindings can reference earlier bindings. |
---|
237 | |
---|
238 | <enscript highlight=scheme> |
---|
239 | (let* ((a 5) |
---|
240 | (b (* 2 a))) |
---|
241 | (+ a b)) |
---|
242 | </enscript> |
---|
243 | |
---|
244 | There are other binding forms, such as {{letrec}} and the so-called |
---|
245 | ''named let''. More information about these forms can be found in |
---|
246 | the [[http://schemers.org/Documents/Standards/R5RS/|Scheme specification]]. |
---|
247 | |
---|
248 | === Procedures |
---|
249 | |
---|
250 | Of course using simple expressions like this is not enough. You'll |
---|
251 | need procedures too. In Ruby, named procedures are actually methods |
---|
252 | on objects, but we can forget about that little detail for now: |
---|
253 | |
---|
254 | Ruby: |
---|
255 | |
---|
256 | <enscript highlight=ruby> |
---|
257 | def pythagoras(a, b) |
---|
258 | Math.sqrt(a**2 + b**2) |
---|
259 | end |
---|
260 | </enscript> |
---|
261 | |
---|
262 | CHICKEN: |
---|
263 | |
---|
264 | <enscript highlight=scheme> |
---|
265 | (define pythagoras |
---|
266 | (lambda (a b) |
---|
267 | (sqrt (+ (* a a) (* b b))))) |
---|
268 | </enscript> |
---|
269 | |
---|
270 | Now that's interesting! Procedures are just regular variables in |
---|
271 | Scheme (a bit like functions in Javascript). We assign a lambda to it. |
---|
272 | We can do that in Ruby too, but it's not pretty: |
---|
273 | |
---|
274 | Ruby: |
---|
275 | some_class.send(:define_method, :pythagoras) {|a, b| Math.sqrt(a**2 + b**2) } |
---|
276 | |
---|
277 | Just like in Ruby the {{def foo}} is shorter than the above, in Scheme |
---|
278 | we have a convenient shorthand for defining procedures too: |
---|
279 | |
---|
280 | <enscript highlight=scheme> |
---|
281 | (define (pythagoras a b) |
---|
282 | (sqrt (+ (* a a) (* b b)))) |
---|
283 | </enscript> |
---|
284 | ==== Recursion and tail-call optimization |
---|
285 | |
---|
286 | In Scheme, recursion is a very important tool. In fact, it is so |
---|
287 | important that the Scheme standard ''demands'' tail call optimization |
---|
288 | (TCO), which basically means that you can have infinite recursion as |
---|
289 | long as the recursive procedure does not need to do anything after it |
---|
290 | returns. That sounds a bit strange, so here's an example: |
---|
291 | |
---|
292 | Ruby: |
---|
293 | |
---|
294 | irb(main):010:0> def add_up_to(num) |
---|
295 | irb(main):011:1> if num.zero? |
---|
296 | irb(main):012:2> 0 |
---|
297 | irb(main):013:2> else |
---|
298 | irb(main):014:2* add_up_to(num - 1) + num |
---|
299 | irb(main):015:2> end |
---|
300 | irb(main):016:1> end |
---|
301 | => nil |
---|
302 | irb(main):017:0> add_up_to(4) |
---|
303 | => 10 |
---|
304 | irb(main):018:0> add_up_to(9999) |
---|
305 | SystemStackError: stack level too deep |
---|
306 | |
---|
307 | CHICKEN: |
---|
308 | |
---|
309 | #;2> (define (add-up-to x) |
---|
310 | (if (zero? x) |
---|
311 | 0 |
---|
312 | (+ (add-up-to (sub1 x)) x))) |
---|
313 | #;3> (add-up-to 4) |
---|
314 | 10 |
---|
315 | #;4> (add-up-to 9999) |
---|
316 | 49995000 |
---|
317 | |
---|
318 | Note the {{+}} in front of the {{(add-up-to (sub1 ...))}}. That is a cue that this is not tail-recursive code: each level of recursion must eventually come back 'up a level' in order to complete the addition, and so the program must keep a live reference to every level of recursion until the final result is computed. |
---|
319 | |
---|
320 | In most other Schemes, however, this will break just like in Ruby, |
---|
321 | because when the {{(+ (add-up-to (sub1 x)) x)}} expression is |
---|
322 | evaluated, the recursive call to {{add-up-to}} creates a new stack |
---|
323 | frame so that when it returns the x can be added to the result. |
---|
324 | |
---|
325 | [This code will 'break' in CHICKEN too, but only for much |
---|
326 | larger numbers. Although CHICKEN doesn't have an arbitrary stack |
---|
327 | depth, if you try (add-up-to) on a large enough number, you'll use up |
---|
328 | all your system memory before getting an answer. Read on for a better |
---|
329 | way to write it.] |
---|
330 | |
---|
331 | All Schemes know that when there is nothing that needs to be done |
---|
332 | after a procedure returns, there is no point in returning to the |
---|
333 | procedure that called it at all: instead it can just return directly |
---|
334 | to the procedure that called the current procedure. So the call can |
---|
335 | be optimized to become a ''goto'', replacing the current stack frame. |
---|
336 | |
---|
337 | Here is a tail-recursive version. Ruby still can't handle it: |
---|
338 | |
---|
339 | irb(main):027:0> def add_up_to(x) |
---|
340 | irb(main):028:1> def inner(x, y) |
---|
341 | irb(main):029:2> if x.zero? |
---|
342 | irb(main):030:3> y |
---|
343 | irb(main):031:3> else |
---|
344 | irb(main):032:3* inner(x-1, x+y) |
---|
345 | irb(main):033:3> end |
---|
346 | irb(main):034:2> end |
---|
347 | irb(main):035:1> inner(x, 0) |
---|
348 | irb(main):036:1> end |
---|
349 | => nil |
---|
350 | irb(main):037:0> add_up_to(9999) |
---|
351 | SystemStackError: stack level too deep |
---|
352 | |
---|
353 | But Scheme can (this works in all Schemes): |
---|
354 | |
---|
355 | #;2> (define (add-up-to x) |
---|
356 | (define (inner x y) |
---|
357 | (if (zero? x) |
---|
358 | y |
---|
359 | (inner (sub1 x) (+ y x)))) |
---|
360 | (inner x 0)) |
---|
361 | #;3> (add-up-to 4) |
---|
362 | 10 |
---|
363 | #;4> (add-up-to 9999) |
---|
364 | 49995000 |
---|
365 | |
---|
366 | Note that the recursive call to {{inner}} isn't nested inside another function call, such as the {{(+ (add-up-to ...))}} in the first version. This is the hallmark of a tail-recursive program. (The astute reader might note that it actually *is* nested inside an {{(if ...)}} procedure, but conditional forms like {{if}} are handled intelligently in tail-recursion. The {{if}} statement itself is not nested inside a procedure call, so all is well.) |
---|
367 | |
---|
368 | As you'll notice, this version is a lot faster in CHICKEN too because |
---|
369 | it does not have to travel back through all those empty "stack |
---|
370 | frames". In the first example, CHICKEN's memory usage increases upon |
---|
371 | every recursion: for large numbers, it will break because it can't |
---|
372 | allocate any more. But in the second example, memory usage will stay |
---|
373 | constant and simply loop forever. |
---|
374 | |
---|
375 | === Blocks |
---|
376 | |
---|
377 | Ruby programmers will be familiar with ''blocks.'' Classic example in |
---|
378 | Ruby is the {{map}} method used to iterate over a collection, executing |
---|
379 | a ''block'' of code for each item in the collection. |
---|
380 | |
---|
381 | Ruby: |
---|
382 | >> [1, 2, 3, 4, 5].map { |x| x * x } |
---|
383 | => [1, 4, 9, 16, 25] |
---|
384 | |
---|
385 | Scheme also contains blocks, though we call them anonymous procedures |
---|
386 | usually. Procedures are created using the {{(lambda args body...)}} body |
---|
387 | form. This syntax is a little more verbose than Ruby's, but the trade off |
---|
388 | is that more than one procedure can be passed as an argument, whereas Ruby |
---|
389 | generally only allows one. |
---|
390 | |
---|
391 | Scheme: |
---|
392 | #;1> (map (lambda (x) (* x x)) '(1 2 3 4 5)) |
---|
393 | (1 4 9 16 25) |
---|
394 | |
---|
395 | A more complicated example involves opening and closing files. Say we |
---|
396 | wanted to create a utility like {{wc -l}} that counts the number of |
---|
397 | lines in a file. In Ruby, it might look something like: |
---|
398 | |
---|
399 | <enscript highlight=ruby> |
---|
400 | count = 0 |
---|
401 | File.open("myfile", 'r') { |fh| |
---|
402 | count += 1 while fh.gets |
---|
403 | } |
---|
404 | puts count |
---|
405 | </enscript> |
---|
406 | |
---|
407 | Similarly, Scheme uses anonymous procedures to create the same behavior: |
---|
408 | |
---|
409 | <enscript highlight=scheme> |
---|
410 | (with-input-from-file "filename" |
---|
411 | (lambda () (port-fold (lambda (line lines-so-far) (+ 1 lines-so-far)) 0 read-line))) |
---|
412 | </enscript> |
---|
413 | |
---|
414 | This Scheme code also showcases some typical functional style, using |
---|
415 | a ''fold'' operation instead of incrementing the value of a variable. |
---|
416 | |
---|
417 | |
---|
418 | == Data types |
---|
419 | |
---|
420 | Now we have a basic grasp of Scheme's syntax, we can have a look at the |
---|
421 | different data types CHICKEN has to offer. We will do this from a Ruby |
---|
422 | perspective. |
---|
423 | |
---|
424 | === Arrays |
---|
425 | |
---|
426 | In Ruby we use arrays for storing lists of things. The obvious Scheme |
---|
427 | equivalent type is the list, you'd think. This is sort of true: |
---|
428 | |
---|
429 | Ruby: |
---|
430 | |
---|
431 | <enscript highlight=ruby> |
---|
432 | x = [1, 2, 3, 4] |
---|
433 | x.map{|y| y + 10 } |
---|
434 | x.each{|y| puts y } |
---|
435 | </enscript> |
---|
436 | |
---|
437 | Scheme: |
---|
438 | |
---|
439 | <enscript highlight=scheme> |
---|
440 | (define x '(1 2 3)) |
---|
441 | (map (lambda (x) (+ x 10)) x) |
---|
442 | (for-each print x) |
---|
443 | </enscript> |
---|
444 | |
---|
445 | Note that Scheme does not have the block scoping bug. Another thing |
---|
446 | that we should note is the first line. We create a list by |
---|
447 | ''quoting'' it. This allows us to enter the list in such a way that |
---|
448 | CHICKEN knows the list is just that; a list, and not a procedure |
---|
449 | application of the procedure called {{1}} on the arguments {{2}} and |
---|
450 | {{3}}. The apostrophe takes care of that. |
---|
451 | |
---|
452 | However, we must always remember that the Scheme list is more like a |
---|
453 | linked list. This means that it is very flexible in how we can add |
---|
454 | things to it and alter it, but it also means that traversing it takes |
---|
455 | more time as more items are added to it. Accessing an element is an |
---|
456 | O(n) operation, where n is the position of the element. |
---|
457 | |
---|
458 | If we want O(1) operations on our lists, we can use a ''vector'': |
---|
459 | |
---|
460 | #;1> (define x (vector 1 2 3 4)) |
---|
461 | #;2> (vector-ref x 2) |
---|
462 | 3 |
---|
463 | #;3> (define y (list 1 2 3 4)) |
---|
464 | #;4> (list-ref y 2) |
---|
465 | 3 |
---|
466 | |
---|
467 | Adding new elements to a vector requires resizing or even copying the |
---|
468 | vector, just like it would in Ruby. So whenever you're contemplating |
---|
469 | using a list type, think about the properties you want the list type |
---|
470 | to have. This may sound odd, but in fact this gives you much more |
---|
471 | flexibility than Ruby, where you have the choice of using an Array, |
---|
472 | or... using an Array. However, as Knuth famously said: "Premature |
---|
473 | optimization is the root of all evil", and you should probably take |
---|
474 | the list solution until it's proven that you ''need'' vectors. Also, |
---|
475 | because Lisp was built on lists, it is very good at manipulating them, |
---|
476 | so they're most likely the most convenient datatype. |
---|
477 | |
---|
478 | CHICKEN also offers you several other types of array-like types, each |
---|
479 | with their own unique time and space properties. Which you'll use |
---|
480 | depends on the task at hand and the situations your system will be |
---|
481 | used under. |
---|
482 | |
---|
483 | ==== List procedures |
---|
484 | |
---|
485 | Lists are, as mentioned before, linked lists. This means they always |
---|
486 | consist of two parts: a head and a tail. We've seen the {{list}} |
---|
487 | procedure which creates lists, but this works on lower primitives: |
---|
488 | |
---|
489 | #;1> (list 1) |
---|
490 | (1) |
---|
491 | #;2> (cons 1 '()) |
---|
492 | (1) |
---|
493 | |
---|
494 | The {{()}} is the empty list. It is itself a list, but it is also a |
---|
495 | single symbol. It serves as the ''end of list marker''. That's why |
---|
496 | the list construction procedure, {{cons}}, can create longer lists too: |
---|
497 | |
---|
498 | #;1> (list 1 2 3 4) |
---|
499 | (1 2 3 4) |
---|
500 | #;2> (cons 1 (cons 2 (cons 3 (cons 4 '())))) |
---|
501 | (1 2 3 4) |
---|
502 | |
---|
503 | To take the head/tail of these lists we have two procedures: |
---|
504 | |
---|
505 | #;1> (car '(1 2 3 4)) |
---|
506 | 1 |
---|
507 | #;2> (cdr '(1 2 3 4)) |
---|
508 | (2 3 4) |
---|
509 | #;3> (cdr (cdr '(1 2 3 4))) |
---|
510 | (3 4) |
---|
511 | #;4> (car (cdr (cdr '(1 2 3 4)))) |
---|
512 | 3 |
---|
513 | #;5> (caddr '(1 2 3 4)) ; combination of car cdr cdr |
---|
514 | 3 |
---|
515 | #;6> (car (car '(1 2 3 4))) |
---|
516 | Error: (car) bad argument type: 1 |
---|
517 | #;7> (cdr (cdr '(1))) |
---|
518 | Error: (cdr) bad argument type: () |
---|
519 | |
---|
520 | Actually, {{cons}} just sticks two things together, so we could also |
---|
521 | stick together two numbers: |
---|
522 | |
---|
523 | #;1> (cons 1 2) |
---|
524 | (1 . 2) |
---|
525 | #;2> (car (cons 1 2)) |
---|
526 | 1 |
---|
527 | #;3> (cdr (cons 1 2)) |
---|
528 | 2 |
---|
529 | |
---|
530 | Two things stuck together are called a ''pair''. By sticking together |
---|
531 | more things without an end of list marker, we can create an ''improper |
---|
532 | list'': |
---|
533 | |
---|
534 | #;1> (cons 1 (cons 2 (cons 3 4))) |
---|
535 | (1 2 3 . 4) |
---|
536 | |
---|
537 | You should not use lists like these unless you know what you're doing, |
---|
538 | because ''all'' list library procedures expect ''proper lists'': lists |
---|
539 | with end markers. CHICKEN supports the full |
---|
540 | [[http://srfi.schemers.org/srfi-1/srfi-1.html|SRFI-1]] out of the box. |
---|
541 | Have a look at that document and compare it to the Ruby standard |
---|
542 | Enumerator and Array methods. Most of the procedures in srfi-1 will |
---|
543 | look ''very'' familiar to you. Here are some examples: |
---|
544 | |
---|
545 | #;1> (use srfi-1) ;; Not needed in Ruby |
---|
546 | ; loading library srfi-1 ... |
---|
547 | #;2> ;; [1, 2, 3] + [4, 5, 6] / [1, 2, 3].concat([4, 5, 6]) |
---|
548 | (append '(1 2 3) '(4 5 6)) |
---|
549 | (1 2 3 4 5 6) |
---|
550 | #;3> (map add1 '(1 2 3 4)) ;; [1, 2, 3, 4].map{|x| x + 1} |
---|
551 | (2 3 4 5) |
---|
552 | #;4> ;; No equivalent because map works on one object: |
---|
553 | (map + '(1 2 3 4) '(5 6 7 8)) |
---|
554 | (6 8 10 12) |
---|
555 | #;5> ;; [1, 2, 3, 4].each{|x| puts x} |
---|
556 | (for-each (lambda (x) (printf "~A\n" x)) '(1 2 3 4)) |
---|
557 | 1 |
---|
558 | 2 |
---|
559 | 3 |
---|
560 | 4 |
---|
561 | #;6> ;; [1, 2, 3, 4, 5, 6].select{|x| x.even? } |
---|
562 | (filter even? '(1 2 3 4 5 6)) |
---|
563 | (2 4 6) |
---|
564 | #;7> ;; [1, 2, 3, 4].inject(""){|str, x| str + x.to_s} |
---|
565 | (fold (lambda (x str) (conc str x)) "" '(1 2 3 4)) |
---|
566 | "1234" |
---|
567 | |
---|
568 | === Symbols |
---|
569 | |
---|
570 | Luckily, you are a Ruby programmer, so we will not have to go through |
---|
571 | the whole "explaining what symbols exactly are" again :) |
---|
572 | Actually, Ruby borrowed symbols from Lisp. |
---|
573 | |
---|
574 | Ruby: |
---|
575 | |
---|
576 | <enscript highlight=ruby> |
---|
577 | :foo |
---|
578 | "blah".to_sym |
---|
579 | :blah.to_s |
---|
580 | </enscript> |
---|
581 | |
---|
582 | Scheme: |
---|
583 | |
---|
584 | <enscript highlight=scheme> |
---|
585 | 'foo |
---|
586 | (string->symbol "foo") |
---|
587 | (symbol->string 'foo) |
---|
588 | </enscript> |
---|
589 | |
---|
590 | As we can see, a symbol is only a quoted variable name! This is the |
---|
591 | origin of symbols and also the reason you can {{send}} symbols |
---|
592 | representing method names to objects in Ruby. Symbols have all the |
---|
593 | same semantics as Ruby's symbols: they can be compared in constant |
---|
594 | time and they take up very little memory space. |
---|
595 | |
---|
596 | === Strings |
---|
597 | |
---|
598 | Strings are simple. Just like in Ruby, we have strings enclosed by |
---|
599 | double quotes: {{"foo"}} works the same in Ruby as it does in CHICKEN. |
---|
600 | CHICKEN's double quoted strings work more like Ruby's single-quoted |
---|
601 | strings, though. There is no string interpolation and other things; |
---|
602 | a string is just a string. |
---|
603 | |
---|
604 | Ruby: |
---|
605 | |
---|
606 | <enscript highlight=ruby> |
---|
607 | x = 10 |
---|
608 | y = "x contains #{x}" |
---|
609 | z = "x contains " + x.to_s |
---|
610 | </enscript> |
---|
611 | |
---|
612 | Scheme: |
---|
613 | |
---|
614 | <enscript highlight=scheme> |
---|
615 | (define x 10) |
---|
616 | (define y (sprintf "x contains ~A" x)) |
---|
617 | (define z (conc "x contains " x)) |
---|
618 | ; Conc automatically converts its arguments to strings. We also could do: |
---|
619 | (define z (string-append "x contains " (->string x))) |
---|
620 | </enscript> |
---|
621 | |
---|
622 | Note that {{->string}} is simply the name of a procedure, including |
---|
623 | the arrow. |
---|
624 | |
---|
625 | It may be important to know that Scheme also has a ''character'' data |
---|
626 | type, unlike Ruby: |
---|
627 | |
---|
628 | Ruby: |
---|
629 | |
---|
630 | irb(main):001:0> "foo"[0] |
---|
631 | => 102 |
---|
632 | |
---|
633 | #;1> (string-ref "foo" 0) |
---|
634 | #\f |
---|
635 | #;2> (char->integer #\f) |
---|
636 | 102 |
---|
637 | |
---|
638 | You will probably not need this data type for your first few Scheme |
---|
639 | programs so we won't go into it deeper here. |
---|
640 | |
---|
641 | ==== String procedures |
---|
642 | |
---|
643 | CHICKEN comes shipped with |
---|
644 | [[http://srfi.schemers.org/srfi-13/srfi-13.html|SRFI-13]], which is a |
---|
645 | library of string procedures which is intended to be a lot like |
---|
646 | SRFI-1, which we already looked at [[#List procedures|a few sections |
---|
647 | ago]]: |
---|
648 | |
---|
649 | #;1> (use srfi-13) ;; Not needed in Ruby |
---|
650 | ; loading library srfi-13 ... |
---|
651 | #;2> ;; "abc" + "def" |
---|
652 | (string-append "abc" "def") |
---|
653 | "abcdef" |
---|
654 | #;3> ;; "abcdef"[-3..-1] |
---|
655 | (string-take-right "abcdef" 2) |
---|
656 | "ef" |
---|
657 | #;4> ;; "abcdef".rjust(10) |
---|
658 | (string-pad "abcdef" 10) |
---|
659 | " abcdef" |
---|
660 | #;5> ;; ["this", "is", "very", "cool"].join(" ") |
---|
661 | (string-join '("this" "is" "very" "cool")) |
---|
662 | "this is very cool" |
---|
663 | #;6> ;; "this is very cool".split(" ") |
---|
664 | ;; NOT from srfi-13 but chicken's extras unit: |
---|
665 | (string-split "this is very cool" " ") |
---|
666 | ("this" "is" "very" "cool") |
---|
667 | |
---|
668 | === Regular expressions |
---|
669 | |
---|
670 | Just like in Ruby, there's a Regex data type, but in CHICKEN there is |
---|
671 | no special syntax for it: |
---|
672 | |
---|
673 | Ruby: |
---|
674 | |
---|
675 | irb(main):001:0> /(.)(.)(\d+)(\d)/.match("THX1138.").to_a |
---|
676 | => ["HX1138", "H", "X", "113", "8"] |
---|
677 | |
---|
678 | CHICKEN: |
---|
679 | |
---|
680 | #;1> (use regex) |
---|
681 | ; loading library regex ... |
---|
682 | #;2> (string-search "(.)(.)(\\d+)(\\d)" "THX1138.") |
---|
683 | ("HX1138" "H" "X" "113" "8") |
---|
684 | |
---|
685 | The {{string-search}} procedure automatically transforms |
---|
686 | the first string to a regexp. You can also do that yourself: |
---|
687 | |
---|
688 | #;3> (string-search (regexp "(.)(.)(\\d+)(\\d)") "THX1138.") |
---|
689 | ("HX1138" "H" "X" "113" "8") |
---|
690 | |
---|
691 | The advantage of doing this is that when you need to match several |
---|
692 | strings you can use the same regexp so it doesn't have to precompile |
---|
693 | the regexp every time you call {{string-search}}. |
---|
694 | |
---|
695 | === Hashes |
---|
696 | |
---|
697 | The final datatype we use a lot in Ruby is the Hash. In CHICKEN there |
---|
698 | are two datatypes you could use instead of the Ruby Hash; |
---|
699 | ''association lists'' (or ''alists'' for short) or ''hash tables''. |
---|
700 | |
---|
701 | ==== Association Lists |
---|
702 | |
---|
703 | Association lists are the simpler Hash like structure in chicken. |
---|
704 | Effectively, alists are standard lists of ''pairs,'' where the |
---|
705 | first item in the pair is the key and the second item is the value. |
---|
706 | Consequently, alists have a nice literal form: |
---|
707 | |
---|
708 | <enscript highlight=scheme> |
---|
709 | '((foo 1) (bar 42) (baz 101)) |
---|
710 | </enscript> |
---|
711 | |
---|
712 | To lookup a value in the alist, use {{assoc}}. For example to check |
---|
713 | if the pair {{(bar 42)}} is in the alist: |
---|
714 | |
---|
715 | #;1> (assoc 'bar '((foo 1) (bar 42) (baz 101))) |
---|
716 | (bar 42) |
---|
717 | |
---|
718 | If the pair is not in the list, you would get the boolean false ({{#f}}). |
---|
719 | If you need more stringent checks, you can also use {{assq}} or {{assv}}, |
---|
720 | learning more about these procedures is an exercise for the reader. |
---|
721 | |
---|
722 | Alists may simplistic, and inquisitive readers may notice that lookup is |
---|
723 | {{O(n)}} time. However, they are convenient and adding new items is a constant |
---|
724 | time operation. You may find they work in may places that you might use a |
---|
725 | small Hash in Ruby. |
---|
726 | |
---|
727 | ==== Hash tables |
---|
728 | |
---|
729 | For more complex hashing operations, CHICKEN supplies true hash tables. |
---|
730 | |
---|
731 | #;11> (define h (make-hash-table)) |
---|
732 | #;12> h |
---|
733 | #<hash-table> |
---|
734 | #;13> (hash-table-set! h 'foo 12) |
---|
735 | 12 |
---|
736 | #;14> (hash-table-set! h 'bar 101) |
---|
737 | 101 |
---|
738 | #;15> (hash-table-ref h 'bar) |
---|
739 | 101 |
---|
740 | #;16> (hash-table-delete! h 'bar) |
---|
741 | #t |
---|
742 | #;17> (hash-table-ref h 'bar) |
---|
743 | Error: (hash-table-ref) hash-table does not contain key |
---|
744 | bar |
---|
745 | |
---|
746 | Hash tables are more powerful overall, but do not offer convenient literal |
---|
747 | notation. If you need to convert from a hash table an alist you can use |
---|
748 | {{hash-table->alist}}. The {{alist->hash-table}} procedure converts in the |
---|
749 | opposite direction. For a complete list of supported procedures, check the |
---|
750 | [[/manual/Unit srfi-69|hash table section]] in the manual. |
---|
751 | |
---|
752 | |
---|
753 | === Booleans |
---|
754 | |
---|
755 | Scheme has a boolean type where {{#f}} is false and {{#t}} is true. |
---|
756 | Its handling of truthiness is a lot like Ruby's; anything that is not |
---|
757 | {{#f}} is treated as being ''true'': |
---|
758 | |
---|
759 | #;1> (if #f |
---|
760 | (print "WTF, it's true") |
---|
761 | (print "It's not true")) |
---|
762 | It's not true |
---|
763 | #;2> (if #t |
---|
764 | (print "Yes, it's really true") |
---|
765 | (print "No, it's not true")) |
---|
766 | Yes, it's really true |
---|
767 | #;3> (if "Some random other value than #f" |
---|
768 | (print "Yes, this is also true") |
---|
769 | (print "No, it's not true")) |
---|
770 | Yes, this is also true |
---|
771 | |
---|
772 | Ruby's {{nil}} does not have a direct equivalent in Scheme. In the |
---|
773 | situations where a ''not present'' value is supposed to be returned, |
---|
774 | usually {{#f}} is used: |
---|
775 | |
---|
776 | #;1> (use srfi-1) |
---|
777 | ; loading library srfi-1 ... |
---|
778 | #;2> (find even? '(3 1 4 1 5 9)) |
---|
779 | 4 |
---|
780 | #;3> (find even? '(1 3 7 9)) |
---|
781 | #f |
---|
782 | |
---|
783 | In cases where a procedure really has no sensible thing to return, we |
---|
784 | use the special ''void'' value, returned by the {{void}} procedure: |
---|
785 | |
---|
786 | #;1> (define (say-hello) |
---|
787 | (print "Hello") |
---|
788 | (void)) |
---|
789 | #;2> (say-hello) |
---|
790 | Hello |
---|
791 | #;3> |
---|
792 | |
---|
793 | As we see, the interpreter understands that there is no proper value |
---|
794 | to return so it displays the prompt immediately without showing the |
---|
795 | result value. The example is a little contrived, because in real code |
---|
796 | we wouldn't explicitly call {{(void)}} because {{print}} already |
---|
797 | returns the {{void}} value. |
---|
798 | |
---|
799 | == Examples |
---|
800 | |
---|
801 | Now we have the tools to make programs, let's look at a few larger |
---|
802 | programs to better appreciate how one would program in CHICKEN. |
---|
803 | |
---|
804 | TODO |
---|
805 | |
---|
806 | == CHICKEN and the Real World |
---|
807 | |
---|
808 | Programming is about more than having a pretty language, so let's look |
---|
809 | at what CHICKEN has to offer for real construction work. |
---|
810 | |
---|
811 | === Eggs |
---|
812 | |
---|
813 | Eggs are to chicken what ''gems'' are to Ruby: installable extensions |
---|
814 | like libraries and programs. The list of [[eggs]] is where you should |
---|
815 | look first when you are going to implement something big. You can |
---|
816 | install an egg almost like you install a gem, as follows: |
---|
817 | |
---|
818 | $ chicken-install matchable |
---|
819 | |
---|
820 | This downloads and installs the egg with the name "matchable". This |
---|
821 | egg has no dependencies, but if it did it would have downloaded and |
---|
822 | installed them as well. |
---|
823 | |
---|
824 | |
---|
825 | == Meta programming |
---|
826 | |
---|
827 | A hot topic in the Ruby community is meta programming and DSLs |
---|
828 | (Domain specific languages). These ideas originated from Lisp, which |
---|
829 | means you can just keep on trucking in CHICKEN! |
---|
830 | |
---|
831 | === Data is code and code is data |
---|
832 | |
---|
833 | The most fundamental concept of Lisp is that code is data. As we've |
---|
834 | seen, procedure calls look like lists. We've also seen that we can |
---|
835 | quote lists to "block" Scheme from interpreting a list as a procedure |
---|
836 | call. We can also turn this around on its head and force a list to |
---|
837 | be evaluated as code: |
---|
838 | |
---|
839 | #;1> (define calculate '(+ 1 2 3 4)) |
---|
840 | #;2> calculate |
---|
841 | (+ 1 2 3 4) |
---|
842 | #;3> (eval calculate) |
---|
843 | 10 |
---|
844 | #;4> |
---|
845 | |
---|
846 | "Big deal", you might say, "Ruby also has eval". But the difference |
---|
847 | is what matters: In Ruby you have to construct strings to be evaluated, |
---|
848 | which means you need to be able to parse strings if you want to change |
---|
849 | a Ruby-program-stored-as-string. In Scheme we can simply hack the |
---|
850 | list. ''The program is stored in a parsed state'', so to speak. |
---|
851 | |
---|
852 | If we would want to change the operator from + to *, we can simply |
---|
853 | do that: |
---|
854 | |
---|
855 | #;1> (eval (cons '* (cdr calculate))) |
---|
856 | 24 |
---|
857 | |
---|
858 | This is much more robust than any regexp hacking or ad-hoc parsing on |
---|
859 | strings you want to evaluate. |
---|
860 | |
---|
861 | === Macros |
---|
862 | |
---|
863 | One of the coolest concepts, but also the one most easily abused is |
---|
864 | ''macros''. Because Scheme stores code as data, you can change the |
---|
865 | code on-the-fly as described above. You can do that at run-time on |
---|
866 | random data through eval, but you can also do it at compile-time on |
---|
867 | your program, which is usually the best time to do it. |
---|
868 | |
---|
869 | Scheme macros ''rewrite'' your code during compile time. They can |
---|
870 | range from simple to complex, with some macros defining entire |
---|
871 | "sublanguages" embedded in Scheme. |
---|
872 | |
---|
873 | Some people call Rails' {{acts_as_foo}} methods "macros". This |
---|
874 | description is not wrong, as these methods do ''rewrite'' your |
---|
875 | classes in a similar way to Scheme macros, but they are not quite |
---|
876 | as powerful. |
---|
877 | |
---|
878 | Here is a simple example of a task that is easy in Scheme, but |
---|
879 | much, much harder using Ruby's eval. Say you were debugging a |
---|
880 | program and found yourself printing out variables at certain |
---|
881 | points in the execution, along with the name of the variable |
---|
882 | so you could tell what you were looking at. |
---|
883 | |
---|
884 | <enscript highlight=scheme> |
---|
885 | (print "myvar: " myvar) |
---|
886 | </enscript> |
---|
887 | |
---|
888 | You decide that repeatedly typing the variable name twice (once to |
---|
889 | indicate which variable, once to get the value) is a waste of time. |
---|
890 | Using a macro, you can quickly and easily abstract away the common |
---|
891 | syntax into one place. |
---|
892 | |
---|
893 | <enscript highlight=scheme> |
---|
894 | (define-syntax ez-debug |
---|
895 | (syntax-rules () |
---|
896 | ((_ var) |
---|
897 | (print 'var ": " var)))) |
---|
898 | |
---|
899 | (define myvar '(this is a list)) |
---|
900 | |
---|
901 | (ez-debug myvar) |
---|
902 | </enscript> |
---|
903 | |
---|
904 | This simply wouldn't be possible with a regular procedure. By the time |
---|
905 | a procedure is called, syntactic information like variables names has |
---|
906 | been optimized away. |
---|