Before the lab
Complete the On your own part of L03
If you have time, complete the Git Bisect part of L03. You won't explicitely need this for the rest of the course, but it is an extremely powerful debugging technique that will serve you well in any serious software development role.
Background
- Quick Tutorial on functions as values
- Beautiful Racket list / recursion explainer
- Getting started with scribble
- Quick tutorial on Modules
- Beautiful Racket modules explainer
- Time
- 10 minutes
- Activity
- Group discussion
Discussion of "before the lab"
The programming languages we will study this term are all dynamically typed. This means that not only the value but also the type of variables can change at runtime. Why does this make testing even more important?
What kind of software problems is testing not well suited to find?
Why might mutable state (e.g. instance variables in Java) make writing unit tests harder?
Compare and Contrast
- Time
- 10 minutes
- Activity
- Group discussion
For each of the following racket
samples, what is the closest
concept from Java
, or roughly how would you do the same thing in
Java
? How are the two similar? How are they different? Try to
classify differences as deep or shallow (completely subjective!)
#lang racket
,#lang slideshow
(+ 1 2)
(rectangle 10 10)
(define r 10)
(define (square x) (rectangle x x))
(colorize (square 10) "red")
(let* ([x 10] [y (+ x 10)]) (* x y))
Functions as values
- Time
- 10 minutes
- Activity
- Demo
Most of you probably read
Part 6 of the Quick Tutorial
because the function series
defined there was used in part 7.
Let's try and reduce the repeated code in this definition.
#lang slideshow
(define (series mk)
(hc-append 4 (mk 5) (mk 10) (mk 20)))
The following has a type error. How to fix it?
(define (series2 mk)
(hc-append 4 (map mk '(5 10 20))))
This really starts to pay off with more repetition
(define (series3 mk)
(apply hc-append 1 (build-list 100 mk)))
It turns out we can test our graphical functions, but it is a bit more work
(module+ test
(require rackunit)
(require racket/serialize)
(check-equal? (serialize (series circle)) (series2 circle)))
Recursion
- Time
- 20 minutes
- Activity
- Individual work
The factorial function is defined recursively as
0! = 1 n! = n * (n-1)!
Fill in the missing expression for the recursive case of the factorial function
#lang racket
(define (fact n)
(cond
[(zero? n) 1]
[else ]))
(module+ test
(require rackunit)
(check-equal? (fact 10) 3628800))
- Use the debugger to find the bug in the following program
#lang racket
(define (list-length list)
(if (empty? list)
0
(+ 1 (list-length list))))
(list-length '(1 2 3))
Scribble
- Time
- 20 min
- Activity
- Demo/Group Discussion
Scribble is one of the markup languages supported by frog. It has the advantage of being easily extensible using Racket.
As I work through the following demo, follow along on your own computer.
Create a new blog entry with
$ raco frog -N "Scribble Demo"
What is the first line of the newly created file?
What does that mean? Scribble is an example of a Domain Specific Language meant for producing documents.
Scribble uses @ syntax That means we can use
@
like an escape back to normal racket syntex.- The simplest form of this is to define abbreviations that expand to strings.
A more interesting use is to define functions that can be invoked in the document. Here is the new syntax we want to support
@hello{} @greet{Cookie Monster}
In both cases we need to define a function, which we can do by just putting
@
in front of a normal racket definition.@(define (hello) "hello")
Things become more useful when we use the existing scribble API. Suppose we want to make lots of lists like
HEADING
- item 1
- item 2
Here's a function to do generate the lists from syntax like
@todo["Shopping" "cheese" "fish" "shuriken"]
@(define (todo hdr . lst) (list (bold hdr) (apply itemlist (map item lst))))
Let's try the function out in our blog entry.
Now let's try to understand what features of Racket and scribble it is using.
[] vs {}
what is '.' doing
what are
itemlist
anditem
Modules
- Time
- 30 minutes
- Activity
- Individual
In Lab 2 we covered
the
Quick tutorial section on modules
We've also been using modules as part of
unit testing. In this section of the lab we we will further explore
modules, submodules, require
and provide
.
- start the fcs-vm-cs2613-dev VM
- make a directory
labs/L04
inside your~/fcshome/cs2613
git repository - create
~fcshome/cs2613/labs/L04/hello.rkt
with the following content
#lang racket
(define (hello) (displayln "Hello world!"))
- You can test your new module by running
(hello)
- Following the racket guide,
replace
#lang racket
with a(module …)
form - add a test
(check-equal? (with-output-to-string hello) "Hello world!\n")
check-equal?
is defined in the modulerackunit
, so to get the above to compile we need to(require rackunit)
. Experiment a bit. Does it matter where you put(require rackunit)
?For now just add this to the module body, don't create a test submodule. Temporarily replace
displayln
withdisplay
to see a familiar bug caught by our test.Make a second file in the same directory, call it
application.rkt
.in
application.rkt
, require your module "hello.rkt", and call '(hello)'This fails, because we didn't export any bindings from "hello.rkt". Add
(provide hello)
. Again, observe that it doesn't matter too much where you put it: order of definition is less important in Racket than in some languages. By convention, bothrequire
andprovide
should be at the top of the module (or submodule).Often unit tests can take a noticable amount of time, or require things like network access not necessarily available to an application. To simulate a long running test, use begin and
sleep
(check-equal? (with-output-to-string hello) (begin (sleep 3) "Hello world!\n"))
Notice now that running "application.rkt" takes a long time. Fix this using the
(module+ test …)
form we already used (leave the test body as is).compare running "application.rkt" and "hello.rkt" from the command line with "racket filename" and "raco test filename".
We can see that just running "racket hello.rkt" doesn't seem to do much; it can be useful to have a module that acts a library (i.e. is loaded with
require
) and also as a program. Add a main submodule so that "racket hello.rkt" also invokes(hello)
Commit and push your files from this section.