#lang plai
;; Make sure you pick the "Use the language declared in the source"
;; language in DrRacket (use the Language|Choose Language ... menu item)

;; This is a comment that continues to the end of the line.
; One semi-colon is enough.

;; A common convention is to use two semi-colons for
;; multiple lines of comments, and a single semi-colon
;; when adding a comment on the same
;; line as code.

#| This is a block comment,
   which starts with "#|"
   and ends with a "|#". Block comments
   can be nested, which is why I can
   name the start and end tokens in this
   comment.
|#

;; #; comments out a single expression
;;  (used below to comment out error examples)
#;(/ 1 0)

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Built-in atomic data

;; Booleans

true
false
#t ; traditional name for true
#f ; ditto for false

;; Numbers

1
0.5
1/2  ; this is a literal fraction, not a division operation
1+2i ; complex number

;; Strings

"apple"
"banana cream pie"

;; Symbols

'apple
'banana-cream-pie
'a->b
'#%$^@*&?!

;; Characters (unlikely to be useful)

#\a
#\b
#\A
#\space  ; same as #\  (with a space after the \)

;; Empty list

empty

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Built-in functions on atomic data

not

+
-
* ; etc.

<
>
=
<=
>=

string-append
string=?
string-ref

eq?      ; mostly for symbols
equal?   ; most other things

char->integer
integer->char

empty?

number?
real?
symbol?
string?
char?

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Basic expression forms

;; Procedure application
;;  (<expr> <expr>*)

(not true)                ; => false

(+ 1 2)                   ; => 3
(< 2 1)                   ; => false
(= 1 1)                   ; => true

(string-append "a" "b")   ; => "ab"
(string-ref "apple" 0)    ; => #\a

(eq? 'apple 'apple)       ; => true
(eq? 'apple 'orange)      ; => false
(eq? (string-append "app" "le")
     "apple")             ; => false, probably

(equal? (string-append "app" "le")
        "apple")          ; => true
(string=? "apple" "apple"); => true

(char->integer #\a)       ; => 97
(integer->char 65)        ; => #\A

(empty? empty)            ; => true

(number? empty)           ; => false
(number? 12)              ; => true
(real? 1+2i)              ; => false

;; Conditionals
;;  (cond
;;    [<expr> <expr>]*)
;;  (cond
;;    [<expr> <expr>]*
;;    [else <expr>])

(cond                     ;
 [(< 2 1) 17]             ;
 [(> 2 1) 18])            ; => 18

(cond                     ; second expression not evaluated
 [true 8]                 ;
 [false (* 'a 'b)])       ; => 8

(cond                     ; in fact, second test not evaluated
 [true 9]                 ;
 [(+ 'a 'b) (* 'a 'b)])   ; => 9

(cond                     ; any number of cond-lines allowed
  [(< 3 1) 0]             ;
  [(< 3 2) 1]             ;
  [(< 3 3) 2]             ;
  [(< 3 4) 3])            ; => 3

(cond                     ; else allowed as last case
  [(eq? 'a 'b) 0]         ;
  [(eq? 'a 'c) 1]         ;
  [else 2])               ; => 2

(cond
  [(< 3 1) 1]
  [(< 3 2) 2])            ; => prints nothing

;; If
;;   (if <expr> <expr> <expr>)

(if (< 3 1)               ; simpler form for single test
    "apple"               ;
    "banana")             ; => "banana"

;; And and Or
;;  (and <expr> <expr> <expr>*)
;;  (or <expr> <expr> <expr>*)

(and true true)           ; => true
(and true false)          ; => false
(and (< 2 1) true)        ; => false
(and (< 2 1) (+ 'a 'b))   ; => false (second expression is not evaluated)

(or false true)           ; true
(or false false)          ; false
(or (< 1 2) (+ 'a 'b))    ; => true (second expression is not evaluated)

(and true true true true) ; => true
(or false false false)    ; => false

(and true 1)              ; => 1, because only `false' is false

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Built-in compound data

;; Lists

(cons 1 empty)            ; => (list 1)
(cons 'a (cons 2 empty))  ; => (list 'a 2)

(list 1 2 3)              ; => (list 1 2 3)
(list 1 2 3 empty)        ; => (list 1 2 3 empty)

(append (list 1 2) empty) ; => (list 1 2)
(append (list 1 2)
        (list 3 4))       ; => (list 1 2 3 4)
(append (list 1 2)
        (list 'a 'b)
        (list true))      ; => (list 1 2 'a 'b true)

(first (list 1 2 3))      ; => 1
(rest (list 1 2 3))       ; => (list 2 3)
(first (rest (list 1 2))) ; => 2

(list-ref '(1 2 3) 2)     ; => 3

;; '(...) creates a list, and it distributes over elements
;;        ditto for '{}
'(1 2 3)                  ; => (list 1 2 3)
'(a b)                    ; => (list 'a 'b)
'((1 2) (3 4))            ; => (list (list 1 2) (list 3 4))
'10                       ; => 10
'{{{c}}}                  ; => (list (list (list 'c)))
(cons 1 2)                ; => (cons 1 2), which is a non-list pair

;; Vectors

(vector 1 2 3 4)            ; => (vector 1 2 3 4)
(vector-ref (vector 1 2) 0) ; => 1

; '#(...) creates a vector, and #(...) is self-quoting
'#(1 2)                   ; => (vector 1 2)
#(1 (2 3))                ; => (vector 1 (list 2 3))
#(a b)                    ; => (vector 'a 'b)

;; Boxes

(box 1)                   ; => (box 1)
(unbox (box 1))           ; => 1

'#&1                      ; => (box 1)
#&1                       ; => (box 1)

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Definitions

;; Defining constants
;;  (define <id> <expr>)

(define PI 3.14159)
(* PI 10)                 ; => 31.4159

;; Defining functions
;;  (define (<id> <id>*) <expr>)

(define (circle-area r)
  (* PI r r))
(circle-area 10)          ; => 314.159

(define (is-odd? x)
  (if (zero? x)
      false
      (is-even? (- x 1))))
(define (is-even? x)
  (if (zero? x)
      true
      (is-odd? (- x 1))))
(is-odd? 12)              ; => false

;; Defining datatypes
;;  (define-type <id>
;;    [<id> (<id> <expr>)*]*)

(define-type Animal
  [snake (name symbol?)
         (weight number?)
         (food symbol?)]
  [tiger (name symbol?)
         (stripe-count number?)])

(snake 'Slimey 10 'rats)  ; => (snake 'Slimey 10 'rats)
(tiger 'Tony 12)          ; => (tiger 'Tony 12)

#;(snake 10 'Slimey 5)    ; => error: 10 is not a symbol

(Animal? (snake 'Slimey 10 'rats)) ; => true
(Animal? (tiger 'Tony 12))         ; => true
(Animal? 10)                       ; => false

(snake-name (snake 'Slimey 10 'rats)) ; => 'Slimey
(tiger-name (tiger 'Tony 12))         ; => 'Tony

(snake? (snake 'Slimey 10 'rats)) ; => true
(tiger? (snake 'Slimey 10 'rats)) ; => false

;; A type can have any number of variants:
(define-type Shape
  [square (side number?)]
  [circle (radius number?)]
  [triangle (height number?)
            (width number?)])

(Shape? (triangle 10 12)) ; => true

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Local binding forms

;; Local
;;  (local [<definition>*] <expr>)

(local [(define x 10)]
  (+ x x))                ; => 20

#;x                       ; => error: unbound identifier

(define (to-the-fourth x)
  (local [(define (squared n)
            (* n n))]
    (* (squared x) (squared x))))
(to-the-fourth 10)        ; => 10000

(local [(define (odd? x)
          (if (zero? x)
              false
              (even? (- x 1))))
        (define (even? x)
          (if (zero? x)
              true
              (odd? (- x 1))))]
  (odd? 12))              ; => false

;; the 'local' can be skipped in the body of a function
;; (or in a case of a 'cond' expression):

(define (to-the-sixteenth x)
  (define (to-the-eighth n)
    (* (to-the-fourth n)
       (to-the-fourth n)))
  (* (to-the-eighth x)
     (to-the-eighth x)))

;; Let (less regular, so not used in this course)
;;  (let ([<id> <expr>]*) <expr>)

(let ([x 10]
      [y 11])
  (+ x y))                ; => 21

(let ([x 0])
  (let ([x 10]
        [y (+ x 1)])
    (+ x y)))             ; => 11

(let ([x 0])
  (let* ([x 10]
         [y (+ x 1)])
    (+ x y)))             ; => 21

;; Datatype case dispatch
;;  (type-case <id> <expr>
;;    [<id> (<id>*) <expr>]*)
;;  (type-case <id> <expr>
;;    [<id> (<id>*) <expr>]*
;;    [else <expr>])

(type-case Animal (snake 'Slimey 10 'rats)
  [snake (n w f) n]
  [tiger (n sc) n])      ; => 'Slimey

(define (animal-name a)
  (type-case Animal a
    [snake (n w f) n]
    [tiger (n sc) n]))

(animal-name (snake 'Slimey 10 'rats)) ; => 'Slimey
(animal-name (tiger 'Tony 12)) ; => 'Tony

#;(animal-name 10)        ; => error: 10 is not an Animal

#;(type-case Food ...)    ; => error: Food is not a defined type

#;(define (animal-weight a)
    (type-case Animal a
      [snake (n w f) w])) ; => error: missing tiger case

(define (animal-weight a)
  (type-case Animal a
    [snake (n w f) w]
    [else false]))

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; First-class functions

;; Anonymous function:
;;  (lambda (<id>*) <expr>)

(lambda (x) (+ x 1))      ; => (lambda (a1) ...)

((lambda (x) (+ x 1)) 10) ; => 11

(define add-one
  (lambda (x)
    (+ x 1)))
(add-one 10)              ; => 11

(define (make-adder n)
  (lambda (m)
    (+ m n)))
(make-adder 8)            ; => (lambda (a1) ...)
(define add-five (make-adder 5))
(add-five 12)             ; => 17
((make-adder 5) 12)       ; => 17

(map (lambda (x) (* x x))
     '(1 2 3))            ; => (list 1 4 9)

(andmap (lambda (x) (< x 10))
        '(1 2 3))         ; => true
(andmap (lambda (x) (< x 10))
        '(1 20 3))        ; => false

(ormap (lambda (x) (< x 10))
       '(1 20 a))         ; => true

;; The apply function may be useful eventually:

(define f (lambda (a b c) (+ a (- b c))))
(define l '(1 2 3))

#;(f l)                   ; => error: f expects 3 arguments
(apply f l)               ; => 0

;; apply is most useful with functions that accept any
;;  number of arguments:

(apply + '(1 2 3 4 5))    ; => 15

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Side-effects
;;  IMPORTANT: in this class using a side-effect is
;;             usually wrong; avoid side-effects
;;             unless explicitly directed

;; set! and begin
;;  (set! <id> <expr>)
;;  (begin <expr>*)

(define count 0)

(set! count (+ count 1))  ; =>
count                     ; => 1

(begin
  (set! count (+ count 1))
  count)                  ; => 2

(local [(define x 0)]     ; note: demonstrates set! in local,
  (begin                  ;       but it's terrible style
    (set! x (list x))     ;
    (set! x (list x))     ;
    x))                   ; => (list (list 0))

;; set-box! is a function:

(define B (box 10))
(set-box! B 12)           ; =>
B                         ; => (box 12)
(unbox B)                 ; => 12

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Testing

;; test, test/pred, and test/exn functions:

(test (+ 5 5) 10)         ; => (list 'good 10 10)
(test (+ 5 4) 10)         ; => (list 'bad 9 10)

(test/pred (* 2 3) even?) ; => (list 'good 6 true)
(test/pred (* 3 3) even?) ; => (list 'bad 9 false)

(test/exn (+ 1 'a) "expects type <number>") ; => (list 'good ....)
(test/exn (+ 1 2) "expects type <number>")  ; => (list 'bad ....)
(test/exn (/ 1 0) "expects type <number>")  ; => (list 'bad ....)

(print-only-errors #t) ; only show testing output when there are failures

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; More information

;; Use the "Racket Documentation" item in DrRacket's "Help" menu
;;   See "The Racket Guide"
;;   and "Programming Languages: Application and Interpretation" (under Teaching)