summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--racket.html.markdown495
1 files changed, 321 insertions, 174 deletions
diff --git a/racket.html.markdown b/racket.html.markdown
index 707919dd..1a02f988 100644
--- a/racket.html.markdown
+++ b/racket.html.markdown
@@ -1,9 +1,14 @@
---
+
language: racket
-author: th3rac25
+filename: learnracket.rkt
+contributors:
+ - ["th3rac25", "https://github.com/voila"]
+ - ["Eli Barzilay", "https://github.com/elibarzilay"]
+
---
-Racket is a general purpose, multi-paradigm programming language in the Lisp/Scheme family.
+Racket is a general purpose, multi-paradigm programming language in the Lisp/Scheme family.
Feedback is appreciated! You can reach me at [@th3rac25](http://twitter.com/th3rac25) or th3rac25 [at] [google's email service]
@@ -13,38 +18,43 @@ Feedback is appreciated! You can reach me at [@th3rac25](http://twitter.com/th3r
;;; Comments
-; Single line comments start with a semicolon
+;; Single line comments start with a semicolon
#| Block comments
can span multiple lines and...
#|
- they can be nested !
- |#
+ they can be nested!
+ |#
|#
-; S-expression comments discard the following expression
-#; "this expression will be discarded" "2nd expression" ; => "2nd expression"
-
+;; S-expression comments discard the following expression,
+;; useful to comment expressions when debugging
+#; (this expression is discarded)
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 1. Primitive Datatypes and Operators
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Numbers
9999999999999999999999 ; integers
+#b111 ; binary => 7
+#o111 ; octal => 73
+#x111 ; hexadecimal => 273
3.14 ; reals
6.02e+23
-1/2 ; rationals
-1+2i ; complex numbers
+1/2 ; rationals
+1+2i ; complex numbers
-; Function application is written (f x y z ...)
-; where f is a function and x, y, z, ... are operands
-; If you want to create a literal list of data, use ' to stop it from
-; being evaluated
+;; Function application is written (f x y z ...)
+;; where f is a function and x, y, z, ... are operands
+;; If you want to create a literal list of data, use ' to stop it from
+;; being evaluated
'(+ 1 2) ; => (+ 1 2)
-; Now, some arithmetic operations
+;; Now, some arithmetic operations
(+ 1 1) ; => 2
(- 8 1) ; => 7
(* 10 2) ; => 20
+(expt 2 3) ; => 8
(quotient 5 2) ; => 2
(remainder 5 2) ; => 1
(/ 35 5) ; => 7
@@ -52,189 +62,233 @@ Feedback is appreciated! You can reach me at [@th3rac25](http://twitter.com/th3r
(exact->inexact 1/3) ; => 0.3333333333333333
(+ 1+2i 2-3i) ; => 3-1i
-;;; Booleans
-#t ; for true
+;;; Booleans
+#t ; for true
#f ; for false -- any value other than #f is true
(not #t) ; => #f
(and 0 #f (error "doesn't get here")) ; => #f
(or #f 0 (error "doesn't get here")) ; => 0
-;;; Characters
+;;; Characters
#\A ; => #\A
-#\λ ; => #\λ
+#\λ ; => #\λ
#\u03BB ; => #\λ
;;; Strings are fixed-length array of characters.
"Hello, world!"
-"Benjamin \"Bugsy\" Siegel" ; backslash is an escaping character
-"λx:(μα.α→α).xx" ; any Unicode character can appear in a string constant
+"Benjamin \"Bugsy\" Siegel" ; backslash is an escaping character
+"Foo\tbar\41\x21\u0021\a\r\n" ; includes C escapes, Unicode
+"λx:(μα.α→α).xx" ; can include Unicode characters
-; Strings can be added too!
+;; Strings can be added too!
(string-append "Hello " "world!") ; => "Hello world!"
-; A string can be treated like a list of characters
+;; A string can be treated like a list of characters
(string-ref "Apple" 0) ; => #\A
-; format can be used to format strings:
+;; format can be used to format strings:
(format "~a can be ~a" "strings" "formatted")
-; Printing is pretty easy
+;; Printing is pretty easy
(printf "I'm Racket. Nice to meet you!\n")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 2. Variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; You can create a variable using define
-; a variable name can use any character except: ()[]{}",'`;#|\
+;; You can create a variable using define
+;; a variable name can use any character except: ()[]{}",'`;#|\
(define some-var 5)
some-var ; => 5
-; You can also use unicode characters
+;; You can also use unicode characters
(define ⊆ subset?)
-(⊆ (set 3 2) (set 1 2 3)); => #t
+(⊆ (set 3 2) (set 1 2 3)) ; => #t
-; Accessing a previously unassigned variable is an exception
-;x ; => x: undefined ...
+;; Accessing a previously unassigned variable is an exception
+; x ; => x: undefined ...
-; Local binding: me is bound to "Bob" only within (let ...)
+;; Local binding: `me' is bound to "Bob" only within the (let ...)
(let ([me "Bob"])
- "Alice"
- me) ; => "Bob"
+ "Alice"
+ me) ; => "Bob"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3. Structs and Collections
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Structs
+;; Structs
(struct dog (name breed age))
-(define my-pet
+(define my-pet
(dog "lassie" "collie" 5))
my-pet ; => #<dog>
(dog? my-pet) ; => #t
(dog-name my-pet) ; => "lassie"
;;; Pairs (immutable)
-; "cons" constructs pairs, "car" and "cdr" extract the first
-; and second elements
+;; `cons' constructs pairs, `car' and `cdr' extract the first
+;; and second elements
(cons 1 2) ; => '(1 . 2)
(car (cons 1 2)) ; => 1
(cdr (cons 1 2)) ; => 2
;;; Lists
-; Lists are linked-list data structures
+;; Lists are linked-list data structures, made of `cons' pairs and end
+;; with a `null' (or '()) to mark the end of the list
+(cons 1 (cons 2 (cons 3 null))) ; => '(1 2 3)
+;; `list' is a convenience variadic constructor for lists
(list 1 2 3) ; => '(1 2 3)
+;; and a quote can also be used for a literal list value
+'(1 2 3) ; => '(1 2 3)
-; Use "cons" to add an item to the beginning of a list
-(cons 4 '(1 2 3)) ; => (4 1 2 3)
+;; Can still use `cons' to add an item to the beginning of a list
+(cons 4 '(1 2 3)) ; => '(4 1 2 3)
-; Use "append" to add lists together
-(append '(1 2) '(3 4)) ; => (1 2 3 4)
+;; Use `append' to add lists together
+(append '(1 2) '(3 4)) ; => '(1 2 3 4)
+
+;; Lists are a very basic type, so there is a *lot* of functionality for
+;; them, a few examples:
+(map add1 '(1 2 3)) ; => '(2 3 4)
+(map + '(1 2 3) '(10 20 30)) ; => '(11 22 33)
+(filter even? '(1 2 3 4)) ; => '(2 4)
+(count even? '(1 2 3 4)) ; => 2
+(take '(1 2 3 4) 2) ; => '(1 2)
+(drop '(1 2 3 4) 2) ; => '(3 4)
;;; Vectors
-; Vectors are fixed-length arrays
+;; Vectors are fixed-length arrays
#(1 2 3) ; => '#(1 2 3)
-; Use "vector-append" to add vectors together
+;; Use `vector-append' to add vectors together
(vector-append #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6)
;;; Sets
-; create a set from a list
+;; Create a set from a list
(list->set '(1 2 3 1 2 3 3 2 1 3 2 1)) ; => (set 1 2 3)
-; Add a member with "set-add"
-(set-add (set 1 2 3) 4); => (set 1 2 3 4)
+;; Add a member with `set-add'
+;; (Functional: returns the extended set rather than mutate the input)
+(set-add (set 1 2 3) 4) ; => (set 1 2 3 4)
-; Remove one with "set-remove"
+;; Remove one with `set-remove'
(set-remove (set 1 2 3) 1) ; => (set 2 3)
-; Test for existence with "set-member?"
+;; Test for existence with `set-member?'
(set-member? (set 1 2 3) 1) ; => #t
(set-member? (set 1 2 3) 4) ; => #f
;;; Hashes
-; Create an immutable hash table (There are also mutables ones)
+;; Create an immutable hash table (mutable example below)
(define m (hash 'a 1 'b 2 'c 3))
-; Retrieve a value
+;; Retrieve a value
(hash-ref m 'a) ; => 1
-; Retrieving a non-present value is an exception
+;; Retrieving a non-present value is an exception
; (hash-ref m 'd) => no value found
-; You can provide a default value for missing keys
+;; You can provide a default value for missing keys
(hash-ref m 'd 0) ; => 0
-; Use "hash-set" to extend a hash table
-(define m2 (hash-set m 'd 4))
+;; Use `hash-set' to extend an immutable hash table
+;; (Returns the extended hash instdead of mutating it)
+(define m2 (hash-set m 'd 4))
m2 ; => '#hash((b . 2) (a . 1) (d . 4) (c . 3))
-; Remember, these hashes are immutable!
-m ; => '#hash((b . 2) (a . 1) (c . 3))
+;; Remember, these hashes are immutable!
+m ; => '#hash((b . 2) (a . 1) (c . 3)) <-- no `d'
-; Use "hash-remove" to remove keys
+;; Use `hash-remove' to remove keys (functional too)
(hash-remove m 'a) ; => '#hash((b . 2) (c . 3))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3. Functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Use lambda to create new functions.
-; A function always returns its last statement.
+;; Use `lambda' to create functions.
+;; A function always returns the value of its last expression
(lambda () "Hello World") ; => #<procedure>
+;; Can also use a unicode `λ'
+(λ () "Hello World") ; => same function
-; (You need extra parens to call it)
+;; Use parens to call all functions, including a lambda expression
((lambda () "Hello World")) ; => "Hello World"
+((λ () "Hello World")) ; => "Hello World"
-; Assign a function to a var
+;; Assign a function to a var
(define hello-world (lambda () "Hello World"))
(hello-world) ; => "Hello World"
-; You can shorten this to:
+;; You can shorten this using the function definition syntatcic sugae:
(define (hello-world2) "Hello World")
-; The () is the list of arguments for the function.
-(define hello
+;; The () in the above is the list of arguments for the function
+(define hello
(lambda (name)
(string-append "Hello " name)))
(hello "Steve") ; => "Hello Steve"
+;; ... or equivalently, using a sugared definition:
+(define (hello2 name)
+ (string-append "Hello " name))
-; You can have multi-variadic functions, too
-(define hello2
- (case-lambda
+;; You can have multi-variadic functions too, using `case-lambda'
+(define hello3
+ (case-lambda
[() "Hello World"]
[(name) (string-append "Hello " name)]))
-(hello2 "Jake") ; => "Hello Jake"
-(hello2) ; => "Hello World"
+(hello3 "Jake") ; => "Hello Jake"
+(hello3) ; => "Hello World"
+;; ... or specify optional arguments with a default value expression
+(define (hello4 [name "World"])
+ (string-append "Hello " name))
-; Functions can pack extra arguments up in a list
+;; Functions can pack extra arguments up in a list
(define (count-args . args)
(format "You passed ~a args: ~a" (length args) args))
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"
+;; ... or with the unsugared `lambda' form:
+(define count-args2
+ (lambda args
+ (format "You passed ~a args: ~a" (length args) args)))
-; You can mix regular and packed arguments
+;; You can mix regular and packed arguments
(define (hello-count name . args)
(format "Hello ~a, you passed ~a extra args" name (length args)))
(hello-count "Finn" 1 2 3)
; => "Hello Finn, you passed 3 extra args"
+;; ... unsugared:
+(define hello-count2
+ (lambda (name . args)
+ (format "Hello ~a, you passed ~a extra args" name (length args))))
+
+;; And with keywords
+(define (hello-k #:name [name "World"] #:greeting [g "Hello"] . args)
+ (format "~a ~a, ~a extra args" g name (length args)))
+(hello-k) ; => "Hello World, 0 extra args"
+(hello-k 1 2 3) ; => "Hello World, 3 extra args"
+(hello-k #:greeting "Hi") ; => "Hi World, 0 extra args"
+(hello-k #:name "Finn" #:greeting "Hey") ; => "Hey Finn, 0 extra args"
+(hello-k 1 2 3 #:greeting "Hi" #:name "Finn" 4 5 6)
+ ; => "Hi Finn, 6 extra args"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 4. Equality
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; for numbers use "="
+;; for numbers use `='
(= 3 3.0) ; => #t
(= 2 1) ; => #f
-; for object identity use "eq?"
+;; for object identity use `eq?'
(eq? 3 3) ; => #t
(eq? 3 3.0) ; => #f
(eq? (list 3) (list 3)) ; => #f
-; for collections use "equal?"
+;; for collections use `equal?'
(equal? (list 'a 'b) (list 'a 'b)) ; => #t
(equal? (list 'a 'b) (list 'b 'a)) ; => #f
@@ -246,20 +300,20 @@ m ; => '#hash((b . 2) (a . 1) (c . 3))
(if #t ; test expression
"this is true" ; then expression
- "this is false" ; else expression
- ) ; => "this is true"
+ "this is false") ; else expression
+; => "this is true"
-; In conditionals, all non-#f values are treated as true
-(member "Groucho" '("Harpo" "Groucho" "Zeppo")) ; => '("Groucho" "Zeppo")
-(if (member "Groucho" '("Harpo" "Groucho" "Zeppo"))
- 'yep
- 'nope) ; => 'yep
+;; In conditionals, all non-#f values are treated as true
+(member 'Groucho '(Harpo Groucho Zeppo)) ; => '(Groucho Zeppo)
+(if (member 'Groucho '(Harpo Groucho Zeppo))
+ 'yep
+ 'nope)
+; => 'yep
-; "cond" chains a series of tests to select a result
-(cond
- [(> 2 2) (error "wrong!")]
- [(< 2 2) (error "wrong again!")]
- [else 'ok]) ; => 'ok
+;; `cond' chains a series of tests to select a result
+(cond [(> 2 2) (error "wrong!")]
+ [(< 2 2) (error "wrong again!")]
+ [else 'ok]) ; => 'ok
;;; Pattern Matching
@@ -269,45 +323,36 @@ m ; => '#hash((b . 2) (a . 1) (c . 3))
[(list 0 _) 'fizz]
[(list _ 0) 'buzz]
[_ #f]))
-
+
(fizzbuzz? 15) ; => 'fizzbuzz
-(fizzbuzz? 37) ; => #f
+(fizzbuzz? 37) ; => #f
;;; Loops
-; looping can be done through recursion
+;; Looping can be done through (tail-) recursion
(define (loop i)
(when (< i 10)
- (printf "i:~a~n" i)
- (loop (add1 i))))
+ (printf "i=~a\n" i)
+ (loop (add1 i))))
+(loop 5) ; => i=5, i=6, ...
-(loop 5) ; => i:5 i:6 ...
-
-; similarly, with a named let
+;; Similarly, with a named let
(let loop ((i 0))
(when (< i 10)
- (printf "i:~a~n" i)
- (loop (add1 i)))) ; => i:0 i:1 ...
-
-;;; Comprehensions
-
-(for/list ([i '(1 2 3)])
- (add1 i)) ; => '(2 3 4)
+ (printf "i=~a\n" i)
+ (loop (add1 i)))) ; => i=0, i=1, ...
-(for/list ([i '(1 2 3)] #:when (even? i))
- i) ; => '(2)
-
-(for/hash ([i '(1 2 3)])
- (values i (number->string i))) ; => '#hash((1 . "1") (2 . "2") (3 . "3"))
+;; See below how to add a new `loop' form, but Racket already has a very
+;; flexible `for' form for loops:
+(for ([i 10])
+ (printf "i=~a\n" i)) ; => i=0, i=1, ...
+(for ([i (in-range 5 10)])
+ (printf "i=~a\n" i)) ; => i=5, i=6, ...
-; To combine iteration results, use "for/fold"
-(for/fold ([sum 0]) ([i '(1 2 3 4)])
- (+ sum i)) ; => 10
+;;; Iteration Over Other Sequences
+;; `for' allows iteration over many other kinds of sequences:
+;; lists, vectors, strings, sets, hash tables, etc...
-;;; Sequences
-
-; "for" allows iteration over sequences:
-; lists, vectors, strings, sets, hash tables, etc...
(for ([i (in-list '(l i s t))])
(displayln i))
@@ -321,136 +366,238 @@ m ; => '#hash((b . 2) (a . 1) (c . 3))
(displayln i))
(for ([(k v) (in-hash (hash 'a 1 'b 2 'c 3 ))])
- (printf "key:~a value:~a ~n" k v))
+ (printf "key:~a value:~a\n" k v))
+
+;;; More Complex Iterations
+
+;; Parallel scan of multiple sequences (stops on shortest)
+(for ([i 10] [j '(x y z)]) (printf "~a:~a\n" i j))
+; => 0:x 1:y 2:z
+
+;; Nested loops
+(for* ([i 2] [j '(x y z)]) (printf "~a:~a\n" i j))
+; => 0:x, 0:y, 0:z, 1:x, 1:y, 1:z
+
+;; Conditions
+(for ([i 1000]
+ #:when (> i 5)
+ #:unless (odd? i)
+ #:break (> i 10))
+ (printf "i=~a\n" i))
+; => i=6, i=8, i=10
+
+;;; Comprehensions
+;; Very similar to `for' loops -- just collect the results
+
+(for/list ([i '(1 2 3)])
+ (add1 i)) ; => '(2 3 4)
+
+(for/list ([i '(1 2 3)] #:when (even? i))
+ i) ; => '(2)
+
+(for/list ([i 10] [j '(x y z)])
+ (list i j)) ; => '((0 x) (1 y) (2 z))
+
+(for/list ([i 1000] #:when (> i 5) #:unless (odd? i) #:break (> i 10))
+ i) ; => '(6 8 10)
+
+(for/hash ([i '(1 2 3)])
+ (values i (number->string i)))
+; => '#hash((1 . "1") (2 . "2") (3 . "3"))
+
+;; There are many kinds of other built-in ways to collect loop values:
+(for/sum ([i 10]) (* i i)) ; => 285
+(for/product ([i (in-range 1 11)]) (* i i)) ; => 13168189440000
+(for/and ([i 10] [j (in-range 10 20)]) (< i j)) ; => #t
+(for/or ([i 10] [j (in-range 0 20 2)]) (= i j)) ; => #t
+;; And to use any arbitrary combination, use `for/fold'
+(for/fold ([sum 0]) ([i '(1 2 3 4)]) (+ sum i)) ; => 10
+;; (This can often replace common imperative loops)
;;; Exceptions
-; To catch an exception, use the "with-handlers" form
-; To throw an exception use "raise"
-(with-handlers
- ([(lambda (v) (equal? v "infinity"))
- (lambda (exn) +inf.0)])
- (raise "infinity"))
+;; To catch exceptions, use the `with-handlers' form
+(with-handlers ([exn:fail? (lambda (exn) 999)])
+ (+ 1 "2")) ; => 999
+(with-handlers ([exn:break? (lambda (exn) "no time")])
+ (sleep 3)
+ "phew") ; => "phew", but if you break it => "no time"
+
+;; Use `raise' to throw exceptions or any other value
+(with-handlers ([number? ; catch numeric values raised
+ identity]) ; return them as plain values
+ (+ 1 (raise 2))) ; => 2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 6. Mutation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Use set! to assign a new value to an existing variable
+;; Use `set!' to assign a new value to an existing variable
(define n 5)
-(set! n 6)
+(set! n (add1 n))
n ; => 6
-; Many Racket datatypes can be immutable or mutable
-; (Pairs, Lists, Strings, Vectors, Hash Tables, etc...)
+;; Use boxes for explicitly mutable values (similar to pointers or
+;; references in other languages)
+(define n* (box 5))
+(set-box! n* (add1 (unbox n*)))
+(unbox n*) ; => 6
+
+;; Many Racket datatypes are immutable (pairs, lists, etc), some come in
+;; both mutable and immutable flavors (strings, vectors, hash tables,
+;; etc...)
-; Use "vector" to create a mutable vector
+;; Use `vector' or `make-vector' to create mutable vectors
(define vec (vector 2 2 3 4))
-; Use vector-set! to update a slot
+(define wall (make-vector 100 'bottle-of-beer))
+;; Use vector-set! to update a slot
(vector-set! vec 0 1)
+(vector-set! wall 99 'down)
vec ; => #(1 2 3 4)
+;; Create an empty mutable hash table and manipulate it
+(define m3 (make-hash))
+(hash-set! m3 'a 1)
+(hash-set! m3 'b 2)
+(hash-set! m3 'c 3)
+(hash-ref m3 'a) ; => 1
+(hash-ref m3 'd 0) ; => 0
+(hash-remove! m3 'a)
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 7. Modules
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Modules let you organize code into multiple files and reusable libraries
+;; Modules let you organize code into multiple files and reusable
+;; libraries; here we use sub-modules, nested in the whole module that
+;; this text makes (starting from the "#lang" line)
+
+(module cake racket/base ; define a `cake' module based on racket/base
-(module cake racket/base ; define a new module 'cake' based on racket/base
-
(provide print-cake) ; function exported by the module
-
+
(define (print-cake n)
(show " ~a " n #\.)
(show " .-~a-. " n #\|)
(show " | ~a | " n #\space)
(show "---~a---" n #\-))
-
- (define (show fmt n ch) ;; internal function
+
+ (define (show fmt n ch) ; internal function
(printf fmt (make-string n ch))
(newline)))
-; Use "require" to import all functions from the module
-(require 'cake)
-(print-cake 3)
-;(show "~a" 1 #\A) ; => error, "show" was not exported
+;; Use `require' to get all `provide'd names from a module
+(require 'cake) ; the ' is for a local submodule
+(print-cake 3)
+; (show "~a" 1 #\A) ; => error, `show' was not exported
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 8. Classes and Objects
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Create a class fish%
+;; Create a class fish% (-% is idomatic for class bindings)
(define fish%
- (class object%
+ (class object%
(init size) ; initialization argument
- (super-new) ; superclass initialization
- ; Field
- (define current-size size)
- ; Public methods
- (define/public (get-size) current-size)
- (define/public (grow amt) (set! current-size (+ amt current-size)))
- (define/public (eat other-fish) (grow (send other-fish get-size)))))
-
-; Create an instance of fish%
-(define charlie
+ (super-new) ; superclass initialization
+ ;; Field
+ (define current-size size)
+ ;; Public methods
+ (define/public (get-size)
+ current-size)
+ (define/public (grow amt)
+ (set! current-size (+ amt current-size)))
+ (define/public (eat other-fish)
+ (grow (send other-fish get-size)))))
+
+;; Create an instance of fish%
+(define charlie
(new fish% [size 10]))
-; Use "send" to call an object's methods
+;; Use `send' to call an object's methods
+(send charlie get-size) ; => 10
(send charlie grow 6)
(send charlie get-size) ; => 16
-
+
+;; `fish%' is a plain "first class" value, which can get us mixins
+(define (add-color c%)
+ (class c%
+ (init color)
+ (super-new)
+ (define my-color color)
+ (define/public (get-color) my-color)))
+(define colored-fish% (add-color fish%))
+(define charlie2 (new colored-fish% [size 10] [color 'red]))
+(send charlie2 get-color)
+;; or, with no names:
+(send (new (add-color fish%) [size 10] [color 'red]) get-color)
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 9. Macros
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Macros let you extend the syntax of the language
-(define-syntax-rule (unless test then else)
- (if test else then))
+;; Macros let you extend the syntax of the language
+
+;; Let's add a while loop
+(define-syntax-rule (while condition body ...)
+ (let loop ()
+ (when condition
+ body ...
+ (loop))))
-(unless (even? 10) "odd" "even") ; => "even"
+(let ([i 0])
+ (while (< i 10)
+ (displayln i)
+ (set! i (add1 i))))
-; Macros are hygienic, you cannot clobber existing variables!
-(define-syntax-rule (swap x y)
- (begin
- (define tmp x)
+;; Macros are hygienic, you cannot clobber existing variables!
+(define-syntax-rule (swap! x y) ; -! is idomatic for mutation
+ (let ([tmp x])
(set! x y)
(set! y tmp)))
-(define tmp 1)
+(define tmp 1)
(define a 2)
(define b 3)
-(swap a b)
-(printf "tmp = ~a; a = ~a; b = ~a~n" tmp a b) ; tmp is unaffected by swap
+(swap! a b)
+(printf "tmp = ~a; a = ~a; b = ~a\n" tmp a b) ; tmp is unaffected
+
+;; But they are still code transformations, for example:
+(define-syntax-rule (bad-while condition body ...)
+ (when condition
+ body ...
+ (bad-while condition body ...)))
+;; this macro is broken: it generates infinite code, if you try to use
+;; it, the compiler will get in an infinite loop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 10. Contracts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Contracts impose constraints on values exported from modules
+;; Contracts impose constraints on values exported from modules
(module bank-account racket
(provide (contract-out
- [deposit (-> positive? any)] ; amount will always be a positive number
+ [deposit (-> positive? any)] ; amounts are always positive
[balance (-> positive?)]))
-
+
(define amount 0)
(define (deposit a) (set! amount (+ amount a)))
(define (balance) amount)
-)
+ )
(require 'bank-account)
(deposit 5)
(balance) ; => 5
-; Any client that attempt to deposit a non-positive amount, will be blamed
-; (deposit -5) ; => deposit: contract violation
-; expected: positive?
-; given: -5
-; more details....
+;; Clients that attempt to deposit a non-positive amount are blamed
+;; (deposit -5) ; => deposit: contract violation
+;; expected: positive?
+;; given: -5
+;; more details....
```
## Further Reading
-Still up for more? Try [Quick: An Introduction to Racket with Pictures](http://docs.racket-lang.org/quick/)
-
+Still up for more? Try [Getting Started with Racket](http://docs.racket-lang.org/getting-started/)