Racket pattern matching of lists

I am trying to do pattern matching with lists, but for some reason I get an unexpected match when I do the following:

> (define code '(h1 ((id an-id-here)) Some text here))
> (define code-match-expr '(pre ([class brush: python]) ...))
> (match code
    [code-match-expr #t]
    [_ #f])
#t

Question: Why does code match code-match-expr?

Practical use-case

I tried this in the Racket REPL, because I actually want to solve another practical problem: using Pollen's pygments wrapping functions to highlight code, which will be output as HTML later on. For this purpose I wrote the following code, where the problem occurs:

(define (read-post-from-file path)
  (Post-from-content (replace-code-xexprs (parse-markdown path))))

(define (replace-code-xexprs list-of-xexprs)
  ;; define known languages
  (define KNOWN-LANGUAGE-SYMBOLS
    (list 'python
          'racket
          'html
          'css
          'javascript
          'erlang
          'rust))
  ;; check if it matches for a single language's match expression
  ;; if it mathces any language, return that language's name as a symbol
  (define (get-matching-language an-xexpr)
    (define (matches-lang-match-expr? an-xexpr lang-symbol)
      (display "XEXPR:") (displayln an-xexpr)
      (match an-xexpr
        [`(pre ([class brush: ,lang-symbol]) (code () ,more ...)) lang-symbol]
        [`(pre ([class brush: ,lang-symbol]) ,more ...) lang-symbol]
        [_ #f]))

    (ormap (lambda (lang-symbol)
             ;; (display "trying to match ")
             ;; (display an-xexpr)
             ;; (display " against ")
             ;; (displayln lang-symbol)
             (matches-lang-match-expr? an-xexpr lang-symbol))
           KNOWN-LANGUAGE-SYMBOLS))

  ;; replace code in an xexpr with highlightable code
  ;; TODO: What happens if the code is in a lower level of the xexpr?
  (define (replace-code-in-single-xexpr an-xexpr)
    (let ([matching-language (get-matching-language an-xexpr)])
      (cond [matching-language (code-highlight an-xexpr matching-language)]
            [else an-xexpr])))

  ;; apply the check to all xexpr
  (map replace-code-in-single-xexpr list-of-xexprs))

(define (code-highlight language code)
  (highlight language code))

1 answer

  • answered 2017-11-14 23:43 Sylwester

    match is syntax and does not evaluate the pattern. Since code-match-expr is a symbol it will bind the whole expression (result of evaluating code) to the variable code-match-expr and evaluate the rest of the expressions as the pattern matches. The result will always be #t.

    Notice that the second pattern, the symbol _, is the same pattern. It also matches the whole expression, but _ is special in the way that it does not get bound like code-match-expr does.

    It's important that your defined variable code-match-expr is never used, but since the match binds a variable with the same name your original binding will be shadowed in the consequent of the match.

    Code that works as you intended might look like:

    (define (test code)
      (match code 
        [`(pre ([class brush: python]) ,more ...) #t]
        [_ #f]))
    
    (test '(h1 ((id an-id-here)) Some text here))
    ; ==> #f
    
    (test '(pre ((class brush: python))))
    ; ==> #t
    
    (test '(pre ((class brush: python)) a b c))
    ; ==> #t
    

    As you see the pattern ,more ... means zero or more and what kind of brackets is ignored since in Racket [] is the same as () and {}.