scalapattern-matchingracketpattern-guards

How do you match with guards in Racket?


In Scala you can do something like this:

def times[A](item: A, number: Int): List[A] = number match {
  case n if n <= 0 => Nil // Nil = '()
  case _ => 
    // equivalent to [_ (cons item (times item (- number 1)))]
    item :: times(item, number - 1)
}

Is it possible to do something like this using Racket's match form? I couldn't find it in the documentation

For those not familiar with Scala, the first case matches if the number is equal to or less than 0, the second case is just a wildcard that matches everything else

in other words, what would I write in the ??? spot to achieve similar functionality to what I described above?

(define (times item number)
  (match number
    [??? '()]
    [_ (cons item (times item (- number 1)))]))

Solution

  • Racket's match has an optional #:when clause that lets you write this almost exactly as you did in Scala:

    (define (times item number)
      (match number
        [n #:when (<= n 0) '()]
        [_ (cons item (times item (- number 1)))]))
    

    I think that answers your question, literally. But more idiomatic Racket would be to use cond for something like this -- where it's a simple conditional test and you don't need any destructuring:

    (define (times item number)
      (cond [(<= number 0) '()]
            [else (cons item (times item (- number 1)))]))
    

    Although I'd probably flip the arms:

    (define (times item number)
      (cond [(positive? number) (cons item (times item (- number 1)))]
            [else '()]))
    

    Of course for something this simple you could use if:

    (define (times item number)
      (if (positive? number)
          (cons item (times item (- number 1)))
          '()))
    

    However I've grown to prefer using cond, as the Racket style guide recommends.