Kricket
Kricket

Reputation: 4179

if-else branching in clojure

I'm teaching myself Clojure.

In a non-FP language, I could easily enough write nested if's, and if I didn't specifically put an else, then control would just flow out of the if block. For example:

Thing myfunc()
{
  if(cond1)
  {
    if(cond2)
      return something;
  }
  return somethingelse;
}

However, in Clojure, there's no return statement (that I know of), so if I write:

(defn myfunc []
  (if (cond1)
      (if (cond2) something))
  somethingelse)

then there's no "return" on "something". It seems to just kind of say, ok, here we have a value, now let's keep on executing. The obvious solution would be to combine the conditions, i.e.:

(if (and (cond1) (cond2))
    something
    somethingelse)

but this gets unwieldy/ugly for large conditions. Also, it would take additional finagling to add a statement to the "else" part of cond1. Is there any kind of elegant solution to this?

Upvotes: 34

Views: 43446

Answers (6)

Thomas Moerman
Thomas Moerman

Reputation: 902

The (imho) most readable approach to solve this problem is provided by the core.match library.

This code snippet:

(defn myfunc []
  (if (cond1)
      (if (cond2) something))
  somethingelse)

becomes:

(ns my.project
  (:require [clojure.core.match :refer [match]]))

(defn myfunc []
  (match [cond1 cond2]
    [true true] something
    [_ _] somethingelse))

Note that you can match on other values than boolean values, like matching on keywords (or any clojure value for that matter), e.g.:

(defn myfunc []
  (match [cond1 some-kw another-kw]
    [true :a :z] foo
    [true :b  _] gee
    [true  _  _] bar 
    [false _  _] giz
    [_     _  _] default))

More examples.

Upvotes: 2

user4813927
user4813927

Reputation:

You could also use the (cond) macro:

(defn length-checker [a b]
  (cond
   (= 3 (count (str a))) (if (= 3 (count (str b)))
                   (println "both numbers are 3 digits long")
                   (println "first number is 3 digits, but the 2nd not!"))
   :else (println "first- or both of the numbers are not 3 digits")))

Upvotes: 0

Konrad Garus
Konrad Garus

Reputation: 54005

This is the subtle difference between imperative and functional approach. With imperative, you can place return in any place of the function, while with functional the best way is to have clear and explicit exeecution paths. Some people (me including) prefer the latter approach in imperative programming as well, recognizing it as more obvious and manageable and less error-prone.

To make this function explicit:

Thing myfunc() {
  if(cond1) {
    if(cond2)
      return something;
  }

  return somethingelse;
}

You can refactor it to:

Thing myfunc() {
  if(cond1 && cond2) {
      return something;
  } else {
    return somethingelse;
  }
}

In Clojure, its equivalent is:

(defn myfunc []
  (if (and cond1 cond2) 
      something
      somethingelse))

If you need an "else", your Java version could become:

Thing myfunc() {
  if(cond1) {
    if(cond2) {
      return something;
    } else {
      return newelse;
    }
  } else {
    return somethingelse;
  }
}

... and its Clojure equivalent:

(defn myfunc []
  (if cond1
      (if cond2 something newelse)
      somethingelse))

Upvotes: 36

nickik
nickik

Reputation: 5881

(if (and (cond1) (cond2))
     something
     somethingelse)

(cond 
    (and (cond1) (cond2)) something
    :else somethingelse)

cond does do this if you want to compare the same thing; in a switch-case you can use condp.

I don't see that kind of code very often, but that's the way to do it.

Upvotes: 20

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91534

Imperative Languages have if-statements that say if this then do that else do that and functional languages have if-expressions that say if this return that else return this. it's a different way of looking at the same idea, that reflects a very different approach to expressing problems. in Functional languages everything has a value, really everything, even if you don't do anything with that value.

When i was making the transition it helped a lot to ask my self "what result should this function return" instead of the question "what should this function do" that i was accustomed to asking.

Upvotes: 16

Nevena
Nevena

Reputation: 707

There is no explicit return statement in Clojure, but your code will "return" on "something" because you don't have any expressions after that if and in Clojure the result of the last expression is used as the function’s return value.

Upvotes: 9

Related Questions