source: project/wiki/chicken-for-ruby-programmers @ 8701

Last change on this file since 8701 was 8701, checked in by sjamaan, 12 years ago

Move the meta stuff to below real-world (we want to use syntax-case eggs later on in meta-programming)

File size: 15.0 KB
3== Introduction
5If you don't know much about chicken yet, please take a moment to read
6the introductory part of [[The User's Manual]].  You're back?  Good!
8=== Paradigm independence
10The most important design feature of Ruby is that it is purely
11object-oriented; everything is an object.  Scheme is ''not'' an
12object-oriented language.  In fact, it does not commit to any
13particular programming paradigm -- it offers ''complete and total
14freedom to the programmer''.  If you decide (a part of) a program is
15best implemented in an object-oriented fashion, you can choose to use
16one of the many object systems.  Have a quick glance at [[Eggs
17Unlimited 3#object-oriented-programming]] to get an impression of the
18diversity of styles of object oriented programming you can use with
19Chicken.  By the way, the list on that page shows all the available
20''eggs'' for Chicken.  We'll explain all about these [[Eggs|later]].
22Besides object-oriented programming, you can also program in a
23procedural fashion (like you would in Pascal, for example) or in a
24functional style (a bit like Haskell or ML) and you can even
25experiment with message-passing like in Erlang or logic programming
26like you would with Prolog.
28=== Origins
30Ruby's origins are firmly rooted in Lisp.  It takes many things and
31ideas from Lisp (symbols, lambdas, eval, metaprogramming, DSLs etc).
32What it doesn't take from Lisp it takes from Smalltalk, which was
33itself inspired by Lisp's clean syntax.  All this means that once
34you're over the initial hump of grokking the syntax, you'll find
35yourself in pretty familiar territory.
37Originally, Ruby started out as an implementation-defined language.
38That is, there was only one implementation (Matz's) and whatever that
39implementation did '''was''' the language spec.  Nowadays, new
40implementations of Ruby are being developed, but as of this writing
41there is still no official specification on Ruby's syntax and
42semantics, by my knowledge.
44Scheme, on the other hand, is a specification-defined language.  There
45is one official language specification which says what Scheme is and
46how it works.  Chicken is simply an implementation of that
47specification.  There is one thing that is important to know right
48now: The Scheme specification is ''extremely'' minimal in design.  It
49tries to define as few language constructs as possible, but these few
50should be so powerful that you will not need any more to make
51programs.  This results in an extremely small spec and a very elegant
52and clean language with very few rules to remember, which makes it
53easy to learn.
55However, in the real world you will need much more than just
56programming constructs, you need things to interact with the operating
57system, networking libraries etcetera.  That's where the difference
58between Scheme and other languages comes in: Every implementation of
59Scheme defines the things it thinks are necessary to build real
60programs with.  Unfortunately, this means most Scheme programs are
61inherently unportable, but it also means that Scheme implementations
62are free to experiment how they want and explore new language
63territory.  This gives each Scheme implementation its uniqueness.
65Chicken's power is in how it extends the Scheme standard.  It has a
66very comfortable interface to C that does not require you to touch a
67single line of C code in order to create bindings to existing C
68libraries, but it also gives you the freedom to embed Chicken in C
69code or C in Chicken code as you want.  It offers a TCP/IP networking
70layer, it has great POSIX interoperability so you can interact with
71the OS. And most importantly: It can compile Scheme code to very
72efficient C code which can itself be compiled to machine code, giving
73you the best of both worlds: a dynamic language which allows you to
74program in the flexible way you are used to with Ruby, but this can be
75compiled for maximum efficiency.
77== Syntax
79=== The basics
81The one thing that is most strikingly different between Ruby and
82Scheme is of course the syntax.  Ruby has an extremely baroque syntax
83that allows you many freedoms in how you would like to write down
84things. Scheme, on the other hand, has only one way in which to write
85a given expression.  Let's start by looking at an example.  First we
86start by firing up an {{irb}} session and typing a little program:
88  irb(main):001:0> # My first program
89  irb(main):002:0* [1, 2, 3, 4].map{|x| x + 1}
90  => [2, 3, 4, 5]
91  irb(main):003:0>
93Now, we fire up a {{csi}} (chicken scheme interpreter) session:
95  #;1> ; My first program
96  (map add1 (list 1 2 3 4))
97  (2 3 4 5)
98  #;2>
100In Scheme, lists are delimited with parentheses with its elements
101separated by spaces. As we can see, everything in Scheme is a list,
102even the expressions that you use to tell it what to do!  An
103expression in Scheme is always a list with the operator on its first
104position and the operands following it.  Procedures that accept no
105arguments are simply a list with only the procedure in it, for example
106{{(newline)}}, which simply displays a newline character.
108This simple rule also means that ''every parenthesis has a meaning''.
109You can't add more parentheses or leave off parentheses like you can
110in most Ruby expressions.  Adding extra parentheses simply applies
111the resulting expression as if it were a procedure:
113  #;2> ((newline))
115  Error: call of non-procedure: #<unspecified>
117          Call history:
119          <syntax>                ((newline))
120          <syntax>                (newline)
121          <eval>          ((newline))
122          <eval>          (newline)       <--
124If {{(newline)}} returned a procedure, it would be called.  But as it
125happens, {{newline}} simply returns an unspecified value which is not
126a procedure and thus can't be applied.  We can also use the result of
127calling a procedure in the call to another procedure:
129  #;3> (add1 2)
130  3
131  #;4> (- 10 (add1 2) 1)
132  6
134We see that arithmetic is a little different from Ruby, as a result of
135the simple rules Scheme has for its syntax.  This may be a little
136awkward, especially with complex calculations:
138  #;5> (* (+ 1 (- 6 2) 2) (- 10 5))
139  35
141In Ruby (and other languages with algebraic syntax) this would have
144  irb(main):002:0> (1 + (6 - 2) + 2) * (10 - 5)
145  => 35
146  irb(main):003:0> # Alternatively:
147  irb(main):004:0* (1 + 6 - 2 + 2) * (10 - 5)
148  irb(main):005:0> # or even:
149  irb(main):006:0* (((1) + ((((6 - 2)))) + 2) * (((10) - ((5)))))
150  => 35
152Both types of syntax have their advantages and disadvantages: The
153Ruby-like syntax is more natural, but you have to think about operator
154precedence rules.  Chicken does not need operator precedence rules
155because the precedence can be determined from the way it's nested, but
156it's less natural for most people (though you get used to it very
159Actually, right now you know almost all there is to know
160about Scheme's syntax!  Chicken has a couple of extensions to the
161basic Scheme syntax, but we won't go into detail here.  Later you'll
162see a couple of handy shorcuts, but this is basically it.
164=== Variables
166Variables are names for things.  Chicken has vary lax rules for
167naming variables.  Actually, ''any string'' is a valid identifier
168as long as you quote it correctly.
172  #;1> x = 10
173  => 10
174  #;2> x
175  => 10
176  #;3> x-y-z = 10
177  NameError: undefined local variable or method `x' for main:Object
178        from (irb):2
182  #;1> (define x 10)
183  #;2> x
184  10
185  #;3> (define x-y-z 10)
186  #;4> x-y-z
187  10
188  #;5> (define %x% 1)
189  #;6> %x%
190  1
191  #;7> (define |a b c| 5)
192  #;8> |a b c|
193  5
195As you can see, because of Scheme's operator rules, symbols that would
196normally be separate tokens designating operators have no special
197meaning so we can use it in the middle of a name.  The convention in
198Scheme is to use the minus sign as word separator (in Ruby, you would
199use an underscore for separating words).  The final example shows how
200any string is allowed as a variable name: if the string contains
201syntax that would mean something else to Scheme you can enclose the
202variable name in pipe symbols.  The pipe symbol itself can be escaped
203with a backslash, if you need it to be part of a variable.
205=== Procedures
207Of course using simple expressions like this is not enough.  You'll
208need procedures too.  In Ruby, named procedures are actually methods
209on objects, but we can forget about that little detail for now:
213<enscript highlight=ruby>
214  def pythagoras(a, b)
215    Math.sqrt(a**2 + b**2)
216  end
221<enscript highlight=scheme>
222  (define pythagoras
223    (lambda (a b)
224      (sqrt (* a a) (* b b))))
227Now that's interesting!  Procedures are just regular variables in
228Scheme (a bit like functions in Javascript).  We assign a lambda to it.
229We can do that in Ruby too, but it's not pretty:
232  some_class.send(:define_method, :pythagoras) {|a, b| Math.sqrt(a**2 + b**2) }
234Just like in Ruby the {{def foo}} is shorter than the above, in Scheme
235we have a convenient shorthand for defining procedures too:
237<enscript highlight=scheme>
238  (define (pythagoras a b)
239    (sqrt (* a a) (* b b)))
242To assign to a pre-existing variable we can also use {{set!}}:
244  #;1> (define x 10)
245  #;2> x
246  10
247  #;3> (set! x 20)
248  #;4> x
249  20
251Top-level variables can also be overwritten by simply redefining them,
252but in some cases you need {{set!}}.  However, set! is a typical
253artifact of an imperative programming style and in clean code you
254want to avoid using it.
256== Data types
258Now we have a basic grasp of Scheme's syntax, we can have a look at the
259different data types Chicken has to offer.  We will do this from a Ruby
262=== Strings
264Strings are simple.  Just like in Ruby, we have strings enclosed by
265double quotes: {{"foo"}} works the same in Ruby as it does in Chicken.
266Chicken's double quoted strings work more like Ruby's single-quoted
267strings, though.  There is no string interpolation and other things;
268a string is just a string.
272  x = 10
273  y = "x contains #{x}"
274  z = "x contains " + x.to_s
278  (define x 10)
279  (define y (sprintf "x contains ~A" x))
280  (define z (conc "x contains " x))
281  ; Or:
282  (define z (string-append "x contains " (->string x)))
284Note that {{->string}} is simply the name of a procedure, including
285the arrow.
287=== Arrays
289In Ruby we use arrays for storing lists of things.  The obvious Scheme
290equivalent type is the list, you'd think.  This is sort of true:
294  x = [1, 2, 3, 4]
295{|y| x + 10 }
296  x.each{|y| puts y }
300  (define x '(1 2 3))
301  (map x (lambda (x) (+ x 10)))
302  (for-each (lambda (x) (display x) (newline)) x)
304Note that Scheme does not have the block scoping bug.  Another thing
305that we should note is the first line.  We create a list by
306''quoting'' it.  This allows us to enter the list in such a way that
307Chicken knows the list is just that; a list, and not a procedure
308application of the procedure called {{1}} on the arguments {{2}} and
309{{3}}.  The apostrophe takes care of that.
311However, we must always remember that the Scheme list is more like a
312linked list.  This means that it is very flexible in how we can add
313things to it and alter it, but it also means that traversing it takes
314more time as more items are added to it.  Accessing an element is an
315O(n) operation, where n is the position of the element.
317If we want O(1) operations on our lists, we can use a ''vector'':
319  #;1> (define x (vector 1 2 3 4))
320  #;2> (vector-ref x 2)
321  3
322  #;3> (define y (list 1 2 3 4))
323  #;4> (list-ref y 2)
324  3
326Adding new elements to a vector requires resizing or even copying the
327vector, just like it would in Ruby.  So whenever you're contemplating
328using a list type, think about the properties you want the list type
329to have.  This may sound odd, but in fact this gives you much more
330flexibility than Ruby, where you have the choice of using an Array,
331or... using an Array.  However, as Knuth famously said: "Premature
332optimization is the root of all evil", and you should probably take
333the list solution until it's proven that you ''need'' vectors.  Also,
334because Lisp was built on lists, it is very good at manipulating them,
335so they're most likely the most convenient datatype.
337Chicken also offers you several other types of array-like types, each
338with their own unique time and space properties.  Which you'll use
339depends on the task at hand and the situations your system will be
340used under.
343=== Symbols
345Luckily, you are a Ruby programmer, so we will not have to go through
346the whole "explaining what symbols exactly are" again :)
347Actually, Ruby borrowed symbols from Lisp.
351  "foo".to_sym
355  (string->symbol "foo")
357To enter them literally, we can use this syntax:
361  :foo
365  'foo
367As we can see, a symbol is only a quoted variable name!  This is the
368origin of symbols and also the reason you can {{send}} symbols
369representing method names to objects in Ruby.  Symbols have all the
370same semantics as Ruby's symbols: they can be compared in constant
371time and they take up very little memory space.
373== Examples
375Let's look at a few larger programs to better appreciate how one would
376program in Chicken.
378  TODO
380== Chicken and the Real World
382Programming is about more than having a pretty language, so let's look
383at what Chicken has to offer for real construction work.
385=== Eggs
387Eggs are to chicken what ''gems'' are to Ruby: installable extensions
388like libraries and programs.  The list of [[eggs]] is where you should
389look first when you are going to implement something big. You can
390install an egg almost like you install a gem, as follows:
392  $ chicken-setup runcmd
394This downloads and installs the egg with the name "runcmd".  This
395egg has no dependencies, but if it did it would have downloaded and
396installed them as well.
399== Meta programming
401A hot topic in the Ruby community is meta programming and DSLs
402(Domain specific languages).  These ideas originated from Lisp, which
403means you can just keep on trucking in Chicken!
405=== Data is code and code is data
407The most fundamental concept of Lisp is that code is data.  As we've
408seen, procedure calls look like lists.  We've also seen that we can
409quote lists to "block" Scheme from interpreting a list as a procedure
410call.  We can also turn this around on its head and force a list to
411be evaluated as code:
413  #;1> (define calculate '(+ 1 2 3 4))
414  #;2> calculate
415  (+ 1 2 3 4)
416  #;3> (eval calculate)
417  10
418  #;4>
420"Big deal", you might say, "Ruby also has eval".  But the difference
421is what matters: In Ruby you have to construct strings to be evaluated,
422which means you need to be able to parse strings if you want to change
423a Ruby-program-stored-as-string.  In Scheme we can simply hack the
424list.  ''The program is stored in a parsed state'', so to speak.
426If we would want to change the operator from + to *, we can simply
427do that:
429  #;1> (eval (cons '* (cdr calculate)))
430  24
432This is much more robust than any regexp hacking or ad-hoc parsing on
433strings you want to evaluate.
435=== Macros
437One of the coolest concepts, but also the one most easily abused is
438''macros''.  Because Scheme stores code as data, you can change the
439code on-the-fly as described above.  You can do that at run-time on
440random data through eval, but you can also do it at compile-time on
441your program, which is usually the best time to do it.
443  TODO
Note: See TracBrowser for help on using the repository browser.