Macros + Exceptions

Macros #

In Scheme, everything is a list. For example, (quotient 1 2) can also be seen as a Scheme list with the elements quotient, 1, and 2. What the ' operator lets us do in Scheme is create a list without evaluating certain options, then delay the evaluation until you need it later. For example:

>>> (define lst (list 'quotient 1 2))
lst
>>> lst
(quotient 1 2)
>>> (eval lst)
0.5
>>> (eval (list 'quotient 1 2))
0.5

Quoting #

There are two ways to quote an expression:

  • Quoting
  • Quasiquoting

Quasiquotes are different because they allow certain elements to be unquoted using ,. This means that names can be bound directly:

(define a 2)
(define b 3)
'(+ ,a b) ; (+ (unquote (a)) b)
`(+ ,a b) ; (+ 2 b)

When you call eval on the quasiquoted expression, it saves the value of a directly into the macro which can be very powerful.

If you try to call (eval '(+ ,a b)) you will get an error because , does not work unless it is in a quasiquote because Scheme.

Exceptions #

Exceptions in Python are used to handle errors. In Python, you most likely would have seen quite a few exceptions (for example ZeroDivisionError, StopIteration, etc.) - there is a way to handle these exceptions.

tryexcept #

To handle an exception (which keeps the program running - it doesn’t make the program stop the moment an error appears), you can use Python’s built-in exception handling.

try:
    <body>
except <exception> as <variable>:
    <body>
[except <exception> as <variable> [...]]
[else]

First, the body in the try suite is executed first, then if an error is thrown that matches any <exception> that you put in, the body corresponding to that suite will be run with <variable> bound to the exception.

Example #

try:
    val = 10/0
except ZeroDivisionError as e:
    print(f'handling {type(e)}') # handling <class 'ZeroDivisionError'>
    val = 0

Example inside a function #

def div(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        result = float('inf')
    return result

div(10, 1) # 10
div(10, 0) # inf
div(10, -1) # -10

What Would Python Do? #

Taken from CS61A Fall 2021 Exceptions slides:

def invert(x):
    inverse = 1/x # Raises a ZeroDivisionError if x is 0
    print('Never printed if x is 0')
    return inverse

def invert_safe(x):
    try:
        return invert(x)
    except ZeroDivisionError as e:
        print('ben')
        return 0

invert_safe(1/0) # Error

try:
    invert_safe(0)
except ZeroDivisionError as e:
    print('tao')
# ben
# this is because the place where the error occurs is in the try except block where 'ben' is printed

Raising Exceptions #

Assert Statements #

assert statements that fail raise an exception of type AssertionError

Raise Statement #

You can raise any type of exception by using the raise statement:

raise <expression>

<exception> must evaluate to a subclass of BaseException (or an instance of one). Exceptions are constructed in the same way as do other classes:

raise ZeroDivisionError("lol wtf u doing")