Error with define in Racket
Asked Answered
H

4

13

I just discovered Racket a few days ago, and I'm trying to get more comfortable with it by writing a little script that generates images to represent source code using #lang slideshow.

I know that when programming in a functional paradigm it's good practice to create almost all your variables with let, but I find that it introduces too many levels of nesting and that Racket's let has an overcomplicated API which requires superfluous parentheses. I'm sure this is to remove ambiguity when using let in more powerful ways, but for my purposes it's just an annoyance. Consequently, I'm creating all my variables with define, and writing blocks with begin if I need to (such as in the body of an if statement).

The problem is that I've repeatedly been getting what seem to be very mysterious errors. I'm sure I'm just making some silly beginner's mistake, being new to the language, but I really can't seem to find the source of the complaint.

Here's the offending code:

(define sub-code (foldr ht-append (rectangle 0 0) (map internal-style (rest code))))

although what we're defining sub-code to seems pretty irrelevant. If I replace it with

(define sub-code '())

I receive the same error. DrRacket is saying that define is being used in an expression context. I understand what this error would normally mean - IE that it would raise when you write code like (print (define x 10)), but I can't see what would trigger it here.

If it helps, this define is at the beginning of a begin block, inside an if statement

(if (list? code)
    (begin
        (define sub-code '())
        ; a few more define statements and finally an expression ))

The specific error message DrRacket is printing is

define: not allowed in an expression context in: (define sub-code (quote ()))

I thought maybe define isn't allowed in begin blocks, but I checked the docs and one of the examples for begin is

(begin
    (define x 10)
    x)

So I don't really know what to do. Thanks in advance!

Homestead answered 25/4, 2013 at 17:45 Comment(0)
C
16

Definitions are allowed in a 'body' context, like in lambda and let among others. The consequent and alternate clauses of if are not body contexts; they are expression contexts and therefore definitions are not allowed.

begin is special - begin in a body context allows definitions, but begin in an expression contexts forbids definitions. Your case falls in to the later.

For example:

(define (foo . args)     #| body context #|)
(define foo (lambda args #| body context |#))
(define (foo . args)
  (let (...)
    #| body context |#))

Syntactic keywords that requires expressions: if, cond, case, and, or, when, unless, do, begin. Check out the formal syntax in any Scheme report (r{4,5,6,7}rs); look for <body>, <sequence>, <command>, and <expression>.

Also, if you need a body context in an expression, just wrap a let syntactic form, as such:

(if test
    (let ()
      (define foo 'foo)
      (list foo foo))
    alternate)
Coloquintida answered 25/4, 2013 at 18:15 Comment(1)
Thanks! Ended up just going with let, but it's useful knowing that begin can morph implicitly like that. My intuition was that (begin foo) would just be shorthand for ((lambda () foo)), but I guess it's more complicated than that.Homestead
R
8

As GoZoner explained, you can't use define in an expression context.

What could you do instead?

Use let:

(if (list? code)
    (let ([x '()])
      x)
    ...

Or it would work with an "empty" let and define:

(if (list? code)
    (let ()
      (define x '())
      x)
    ...

But that's a bit silly.

Or use cond and define:

(cond [(list? code)
       (define x '())
       x]
      ...

This last way -- using cond and define -- is closest to what the current Racket style guide recommends.

Refute answered 25/4, 2013 at 18:29 Comment(3)
cond does not establish an expression context (according to my test in R6RS and the R7RS draft spec).Coloquintida
In Racket (cond [#t (define x 1) x]) is valid and returns 1.Refute
Well, we all know ...Racket is not Scheme... :-) In Ikarus (cond (#t (define foo 'foo) (list foo foo))) -> "Error: A definition was found where an expression was expected."Coloquintida
S
4

Here's more details, from the Racket docs.

The different contexts are required because macros must expand differently, depending on which language forms are allowed.

As others have said, definitions are not allowed in expression contexts ("expr ..." in the docs), but are ok in other contexts.

In other doc entries, "body ..." indicates an internal-definition context (guide, reference), for example in lambda bodies, and "form ..." indicates all non-expression contexts, like in the docs for begin.

Scarab answered 25/4, 2013 at 19:28 Comment(0)
F
-1

Or you could wrap the expressions in (begin) e.g.(begin (define x 10) (define y 100) (define z 1000))

Fourteen answered 11/11, 2015 at 5:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.