Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are pattern matching or polymorphic dispatch available as a conditional structures in clojure?

Tags:

clojure

In a static language, I can replace conditional with polymorphism.

In languages like erlang I can use pattern matching instead of if else.

What can I use in clojure?

like image 622
dagda1 Avatar asked Dec 03 '22 19:12

dagda1


1 Answers

Both pattern matching and polymorphic dispatch are available.

Two forms of polymorphic dispatch are multimethods and protocols.

leontalbot gave a decent example of multimethods that dispatch to specific implementation based on some specific properties of arguments (could be type, but they can use different dispatch functions). In other words, to decide which implementation to use multimethod executes a function on the argument and compares that to dispatch table.

Here's another example:

; Declare multimethod:
(defmulti get-length class)

; Provide implementations for concrete types:
(defmethod get-length java.lang.String [str] (.length str))
(defmethod get-length java.lang.Number [num] (.length (str num)))

; Call it for String:
(get-length "Hello") ;=> 5

; Call it for a Number (works because Long is a subtype of Number):
(get-length 1234) ;=> 4

We're using trivial examples here to keep things simple, but the dispatch function can be more interesting. For another example, let's say we want to choose sorting algorithm based on input length:

(defn choose-impl [in]
  (cond
    (is-sorted? in) :sorted
    (< (count in) 10) :bubble
    :else :quicksort))

(defmulti my-sort choose-impl)

(defmethod my-sort :sorted [in] in)

(defmethod my-sort :bubble [in] (my-bubble-sort in))

(defmethod my-sort :quicksort [in] (my-quicksort in))

This one is contrived and you probably wouldn't implement it this way, but I hope it's good example of using different dispatch functions.

Protocols are a different thing, more like interfaces known from Java and other OO languages. You define a set of operations that form a protocol, and then provide implementations for various types.

; Protocol specification:
(defprotocol my-length (get-length [x]))

; Records can implement protocols:
(defrecord my-constant-record [value]
  my-length
    (get-length [x] value))

; We can "extend" existing types to support the protocol too:
(extend-protocol my-length
  java.lang.String
    (get-length [x] (.length x))
  java.lang.Long
    (get-length [x] (.length (.toString x))))

; Now calling get-length will find the right implementation for each:
(get-length (my-constant-record. 15)) ;=> 15

(get-length "123") ;=> 3

(get-length 1234) ;=> 4

Finally, pattern matching is available with the very popular core.match library:

(doseq [n (range 1 101)]
  (println
    (match [(mod n 3) (mod n 5)]
      [0 0] "FizzBuzz"
      [0 _] "Fizz"
      [_ 0] "Buzz"
      :else n)))
like image 64
Konrad Garus Avatar answered Dec 21 '22 04:12

Konrad Garus