clojureclojure-core.match

Core.match with guards on a map key / value


Is it possile to use core.match to do the following (silly example):

  (defn summaries-film [film]
     (match film
         {:genre "Horror" :budget :guard #(< % 1000000) :star _} "Low budget horror"
         {:genre "Comedy" :budget _ :star "Adam Sandler"} "Trash"
         {:genre _ :budget _ :star "Emily Blunt"} "5 Stars"
         :else "Some other film"))

??

I'd like to be able to pattern match over a map, but then have the :guard #(< % 10000) bit? i.e. have a function in the pattern based on the value of the key in the map?

Is this possible, I know I can do this over a vector, but can't work out the syntax or if its possible with maps.

I know I can use destructuring, but I'd like to know if it's possible with pattern matching.

Thanks


Solution

  • You can use guards with maps, though the syntax is different. Wrap the pattern in a list and append :guard your-guard-fn. The guard function will be invoked with the entire map, assuming the pattern matches otherwise:

    ({:foo 1} :guard #(= 1 (:foo %)))
    

    Here's what it looks like with your example:

    (defn summaries-film [film]
      (match film
        ({:genre "Horror" :budget _ :star _} :guard #(< (:budget %) 100)) "Low budget horror"
        {:genre "Comedy" :budget _ :star "Adam Sandler"} "Trash"
        {:genre _ :budget _ :star "Emily Blunt"} "5 Stars"
        :else "Some other film"))
    
    (summaries-film {:genre "Horror" :budget 1 :star "Kelsey Grammer"})
    ;=> "Low budget horror"
    (summaries-film {:genre "Horror" :budget 101 :star "Robert Forster"})
    ;=> "Some other film"