Search code examples
schememit-schemeguile

scheme read function's behavior


I can't understand this scheme read function's behavior.

gosh> (null? '())
#t
gosh> (null? (read))
'()
#f
gosh> (define a (read))
'()
a
gosh> a
'()
gosh> (null? a)
#f

I expected (null? (read)) is #t(true) when I inputted '(). Not only Gauche, but also MIT-Scheme and GNU/Guile behaves like this.


Solution

  • The read function doesn't evaluate the text its given, it treats it as an unevaluated expression and returns the expression tree as data. For example:

    (read)  ;input: (+ 4 5)
    

    Is equivalent to (list '+ 4 5):

    > (read)  ;input: (+ 4 5)
    (+ 4 5)
    > (list '+ 4 5)
    (+ 4 5)
    > (equal? (read) (list '+ 4 5))  ;input: (+ 4 5)
    #t
    

    From now on I'll use the ;= syntax to demonstrate how you would write it with constructors like list.

    > (read)  ;input: (+ 4 5)
    ;= (list '+ 4 5)
    (+ 4 5)
    

    Now, because read returns the data representation for the expression its given, it "adds a quote level". By that I mean that to write an equivalent expression, you would have to wrap quote around it.

    > (read)  ;input: (+ 4 5)
    ;= (list '+ 4 5)
    ;= '(+ 4 5)
    > (read)  ;input: (quote (+ 4 5))
    ;= (list 'quote (list '+ 4 5))
    ;= '(quote (+ 4 5))
    ;= ''(+ 4 5)
    

    Because read and write deal with unevaluated expressions, the read function will seem to add a quote level, and the write function will seem to remove a quote level:

    > (write '(+ 4 5))
    ;output: (+ 4 5)
    > (write ''(+ 4 5))
    ;output: '(+ 4 5)
    > (write '''''(+ 4 5))
    ;output: ''''(+ 4 5)
    

    The read function does the opposite, seeming to add a quote level. However, this is made more obscure by the fact that the Scheme Repl uses write, which "removes" it again. However, if you concentrate on the ;= expressions it's more clear why read and write are opposites:

    > (read)  ;input (+ 4 5)                  (no quotes)
    ;= '(+ 4 5)                               (one quote)
    (+ 4 5)                                   (the `write` function removes a quote)
    > (read)  ;input '(+ 4 5)                 (one quote)
    ;= ''(+ 4 5)                              (two quotes)
    '(+ 4 5)                                  (the `write` function removes a quote)
    > (read)  ;input '''(+ 4 5)               (three quotes)
    ;= ''''(+ 4 5)                            (four quotes)
    '''(+ 4 5)                                (the `write` function removes a quote)
    

    Now back to your example:

    > (read)  ;input: '()
    ;= ???
    ???
    

    Since read adds a quote to represent an expression as data, reading '() is equivalent to ''().

    > (read)  ;input: '()
    ;= ''()
    ;= (list 'quote (list))
    '()
    > (define a (read))  ;input: '()
    > a
    ;= ''()
    ;= (list 'quote (list))
    '()
    > (equal? a ''())
    #t
    > (equal? a (list 'quote (list)))
    #t
    

    To actually get something equivalent to '() so that it will be null?, you have to give () as input.

    > (read)  ;input: ()
    ;= '()
    ;= (list)
    ()
    > (define a (read))  ;input: ()
    > a
    ;= '()
    ;= (list)
    ()
    > (equal? a '())
    #t
    > (null? a)
    #t