Search code examples
schemeracketcontrol-flow

Why can't I return the form with cond as I can with if-else?


Inspired by the syntax of if from the exercise 1.4 from SICP, I tried to code something similar but with cond:

#!/usr/bin/env racket
#lang racket/base

(define (b x) (display (bytes-append #"\033[1m" (string->bytes/utf-8 x) #"\33[m")))
(define (i x) (display (bytes-append #"\33[3m" (string->bytes/utf-8 x) #"\33[m")))
#|
(define (formatted-printer s)
  ((if (equal? s "You have to be bold to do it.\n") b i) s)); Works fine, preferred as it is concise. Do the same with cond!!!
(define (formatted-printer s)
  (if (equal? s "You have to be bold to do it.\n") 
    (b s)
    (i s))); Works fine, too verbose, though.|#

#|
(define (formatted-printer s); Works fine
  (cond 
    ((equal? s "You have to be bold to do it.\n") (b s))
    ((equal? s "You have to be Italian in order to eat pasta.\n") (i s))
    ))
|#
(define (formatted-printer s)
  ((cond 
    ((equal? s "You have to be bold to do it.\n") b)
    ((equal? s "You have to be Italian in order to eat pasta.\n") i)
      s)));Does not work: cond: bad syntax (clause is not a test-value pair) 


    
(formatted-printer "You have to be bold to do it.\n")
(formatted-printer "You have to be Italian in order to eat pasta.\n")

Sadly, when I try to apply double parentheses around cond, as it did the trick around if, I get:

cond: bad syntax (clause is not a test-value pair)
  at: s
  in: (cond ((equal? s "You have to be bold to do it.\n") b) ((equal? s "You have to be Italian in order to eat pasta.\n") i) s)
  location...:

The comments hint what I am aiming at. Is it at all doable to select with cond only the operator like it is with if?


Solution

  • In your if version you are ending the if form aplying the result with s as the operand. In the cond version you don't. It should be like this:

    (define (formatted-printer s)
      ((cond 
        ((equal? s "You have to be bold to do it.\n") b)
        ((equal? s "You have to be Italian in order to eat pasta.\n") i)
        (else (error "no matching string for formatted-printer")))
       s))
    

    Notice the else. If you don't have that you can in the future end up with a weird application: not a procedure for #<void> since if none of the strings match and you don't have an else Racket will use its undefined value as the result and that can not be called as a procedure.

    You can even make it more clear:

    (define (formatted-printer s)
      (define formatter
        (cond 
          ((equal? s "You have to be bold to do it.\n") b)
          ((equal? s "You have to be Italian in order to eat pasta.\n") i)
          (else (error "no matching string for formatted-printer"))))
      (formatter s))
    

    Since you're using Racket and probably DrRacket you can even debug this much easier with adding a breakpoint at (formatter s) to investigate the result of what formatter was set to.