4

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?

Community
  • 1
  • 1
dagda1
  • 26,856
  • 59
  • 237
  • 450
  • 4
    Maybe, [multimethods](http://clojure.org/multimethods)? That's, actually, Clojure's version of polymorphism. – bereal Jul 10 '14 at 15:22
  • 1
    this question is really to broad to answer – soulcheck Jul 10 '14 at 15:27
  • 1
    for pattern matching you have: https://github.com/clojure/core.match – soulcheck Jul 10 '14 at 15:28
  • 3
    Yeah, this is super broad. multimethods and protocols both "replace if" in exactly the way this article describes. Further, clojure is able to generate subclasses of java classes or a class implementing a java interface, which behave similarly. `fnil` replaces an if check, as does the optional third arg to get (second arg to map or keyword used in function position) – noisesmith Jul 10 '14 at 15:32
  • @soulcheck & noisesmith if you guys think this question is too broad, why aren't you voting to put it on hold as too broad? – amalloy Jul 10 '14 at 21:01
  • @amalloy You could have used your dup hammer I suppose: http://stackoverflow.com/q/9657707/1756702 or http://stackoverflow.com/q/13478064/1756702 – A. Webb Jul 10 '14 at 21:37
  • @A.Webb I don't think either of those are duplicates. The first is asking why to avoid `if`, not how - the opposite question! The second is a plausible candidate, but doesn't really feel like a dupe to me. – amalloy Jul 10 '14 at 21:40
  • This is a clearly specified question with several straightforward--i.e. simple, very specific--answers, all mentioned in previous comments. Why does that make the question too broad? OP is not asking for every possible way to replace `if` in every possible situation. She/he is asking for general-purpose methods, and gives an illustration of the sort of thing s/he has in mind. Moreover, answers to this question will be informative for other Clojure novices. (leontalbot's answer is one of several suitable answers.) – Mars Jul 11 '14 at 04:16

2 Answers2

10

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)))
Konrad Garus
  • 53,145
  • 43
  • 157
  • 230
7

You want to use multimethods. This is an excellent article explaining you how to use those. http://adambard.com/blog/structured-clojure-protocols-and-multimethods/

(def speed 24)

(defmulti get-speed :type)

(defmethod get-speed :european       [_] speed)
(defmethod get-speed :african        [_] (+ speed 2))
(defmethod get-speed :norwegian-blue [_] (* speed 1.05))

(get-speed {:type :european}) 
; => 200
leontalbot
  • 2,513
  • 1
  • 23
  • 32
  • 2
    There seems to be a bug: according to [this research](http://style.org/unladenswallow/), "average cruising airspeed velocity of an unladen European Swallow is roughly 11 meters per second, or 24 miles an hour". – bereal Jul 11 '14 at 06:57
  • @bereal Well, what I wanted to do here is give a simple example of how multimethods work. If you want this answer to "bereal", please suggest edit for african and norwegian speeds too. ;-) – leontalbot Jul 11 '14 at 17:46