You're running into the difference between declaring a function and declaring a variable.
-
(define next-color "green")
creates a variable named 'next-color' and assigns it the value "green"
, which is a string
-
(define next-color (lambda (c) "green")
creates a variable named 'next-color' and assigns it the value (lambda (c) "green")
, which is a function that returns the string "green"
-
(define (next-color c) "green")
is a shortcut for the function-creation version immediately above. It is the most common way to create functions in Racket.
(See footnote [1])
Doing this: (next-color "green")
means that you want to call the function next-color
with the argument "green"
and have it return the result.
Doing this: next-color
means that you want to put the string "green"
at that location in your program's execution.
You have defined next-color
to be a variable but you are using it as a function. Don't do that.
I'm assuming that you want to define a variable the value of which is determined by the if
statement, then later change the value of that variable to be "green"
. The correct syntax for that would be:
(define color "amber")
(define next-color (if (string=? "green" color) "yellow" color)) ; create the variable, assign it
(set! next-color "green") ; change the contents of the variable
That will work, but it's not very Rackety. Racket is a functional programming language, meaning that values do not change once they are created. If you want to have a new value, you create a new variable. set!
is a mutation operator, and Racket wants to avoid mutation because programs are easier to understand and less bug-prone when they do not use mutation. It means that everyone involved can look at the contents of a variable and rely on those contents always staying the same.
There are a lot of Rackety ways to do this depending on what you're trying to accomplish. One way would be to use a parameter, like so:
; This is the naive version that you should not use
(define next-color (make-parameter "amber")) ; a parameter is a special kind of function that holds a value
(next-color) ; used with no arguments, evaluating the parameter will return its contents, in this case the string "amber"
(next-color "green") ; used with an argument, the parameter's value is updated to the new value
(next-color) ; this time it will return the string "green"
This does what you were looking for but it should be avoided because it's effectively mutating the value and that mutation will be visible from every part of your program that uses the parameter. A better way to do it is this:
; This is the right way to use a parameter
(define next-color (make-parameter "amber"))
(next-color) ; returns "amber"
(parameterize ([next-color "green"]) ; creates a new scope, changes the contents of the parameter within that scope, automatically reverts the contents after you leave the scope
(next-color) ; returns "green"
) ; closes the 'parameterize', reverts the parameter's contents
(next-color) ; returns "amber"
Parameters are a bit heavyweight. Here's a simpler method:
(define next-color "amber")
(let ([next-color "green"]) ; creates a new scope. creates a new variable within this scope. the new variable is named 'next-color' but it is not the same 'next-color' that was defined above
(display next-color) ; prints the string "green" to the screen
) ; closes the 'let', gets rid of the 'next-color' variable that was created for it
(display next-color) ; prints the string "amber" to the screen because we are back to the scope outer scope where (define next-color "green") was used
Does that help?
[1] Instead of saying that a value is assigned to a variable, Racket says that a value is bound to an identifier. I'm using variable and assign because I suspect they will be more familiar. The difference is not worth going into here.