hyper.dev [feed] [resume]

2020/02/11 - Arew Scheme Tutorial: Forward

Continuation

After reading this section you will be able to write more complex Scheme code. In particular you will study:

How to create lexical bindings

Lexical bindings can be created with let, let*, letrec and letrec*. They have slightly different behaviors, but the same syntax:

(let (<binding> ...) <expression> ...)

Where <binding> looks like an association of a variable name with the initial value it is holding. For instance:

(let ((a 1)
      (b 2))
  (+ a b 3)) ;; => 6

The above let form will bind a to 1, b to 2 and return the output of (+ a b 3) that is 6.

How to set a variable

To change what a variable holds without overriding it or mutating the object contained in the varialbe, you can use set!. Mind the exclamation mark, it is a convention that forms that have a side-effect ends with a exclamation mark. For instance:

(define %thruth 42)

(display %truth)
(newline)

(set! %thruth 101)

(display %truth)
(newline)

How to do a if

Scheme if will consider false, only the object #f. Hence, one can do the following:

(if #t
  (display "true")
  (display "never executed"))

Similarly:

(if #f
  (display "never executed")
  (display "false"))

In particular, the number zero is true according to if:

(if 0
  (display "zero is true")
  (display "never executed"))

If you want to check whether a value is zero you can use the predicate zero? like so:

(if (zero? %thruth)
   (display "%thruth is zero")
   (display "%thruth is not zero"))

Or the less idiomatic predicate =:

(if (= %truth 0)
  (display "%thruth is zero")
  (display "%thruth is not zero"))

How to create a new type

To create a new type you can use the macro define-record-type. For instance, in a todo list application, we will need an <item> type that can be defined as:

(define-record-type <item>
  (make-item title body status)
  item?
  (title item-title item-title!)
  (body item-body item-body!)
  (status item-status item-status!))

Where:

Here is an example use of the above <item> definition:

(define item (make-item "Learn Scheme" "The Scheme programming language is awesome, I should learn it" 'todo))

;; To change the status, one can do the following:

(item-status! item 'wip)

;; to get the title, one can do the following:

(display (item-title item))
(newline)

How to write a named-let

A named-let allows to do recursion without going through the ceremony of defining a separate procedure. In pratice, it used in similar contexts such as for or while loop in other languages. Given the procedure (cons item lst) that will return a new list with LST as tail and ITEM as first item, study the following code:

(let loop ((index 0)
           (out '())
  (if (= index 10)
      (display out)
      (loop (+ index 1) (cons index out))))

It is equivalent to the following:

(define (loop index out)
  (if (= index 10)
      (display out)
      (loop (+ index 1) (cons index out))))

(loop 0 '())

A named-let, look like a let form that can be used to bind variables prefixed with a name. Here is some pseudo-code that describe the syntax of the named-let form:

(let <name> (<binding> ...) expression ...))

So <binding> and <expression> are very similar to a let. <name> will be bound to a procedure that takes as many argument as there is <binding> and its body will be <expression> .... It will be called with the associated objects in <binding> .... expression can call <name> most likely in tail call position but not necessarly. If the named-let is not tail-recursive it is to be a grow the stack recursive call. Another way to see the named-let is pseudo-code:

(define <name> (lambda <formals> <expression> ...))

(<name> <arguments> ...)

Where:

Backtrack

(define-record-type <record-name>
  (make-record-name field0 ...)
  record-name?
  (field0 record-name-field0 record-name-field0!))
(let loop ((index 0))
  (display index)
  (loop (+ index 1)))