48
votes

Common Lisp has return-from; is there any sort of return in Clojure for when you want to return early from a function?

8
Clojure code tends not to be structured as a series of statements, so returning early is not necessarily as meaningful. If you'd like to give an example of where you're wanting it, people might be able to suggest alternatives.Chuck

8 Answers

56
votes

When you need to bail out of a computation early, you need a way to do that, not an argument from purists. Usually you need it when you're reducing a big collection and a certain value indicates that there's no point in further processing the collection. To that end, the ever-practical Clojure provides the reduced function.

A simple example to illustrate is that when multiplying a sequence of numbers, if you encounter a zero, you already know that the final result will be zero, so you don't need to look at the rest of the sequence. Here's how you code that with reduced:

(defn product [nums]
  (reduce #(if (zero? %2)
               (reduced 0.0)
               (* %1 %2))
          1.0
          nums))

reduced wraps the value you give it in a sentinel data structure so that reduce knows to stop reading from the collection and simply return the reduced value right now. Hey, it's pure-functional, even!

You can see what's going on if you wrap the above if in a do with a (println %1 %2):

user=> (product [21.0 22.0 0.0 23.0 24.0 25.0 26.0])
1.0 21.0
21.0 22.0
462.0 0.0
0.0

user=> (product [21.0 22.0 23.0 24.0 25.0 26.0])
1.0 21.0
21.0 22.0
462.0 23.0
10626.0 24.0
255024.0 25.0
6375600.0 26.0
1.657656E8
31
votes

There isn't any explicit return statement in clojure. You could hack something together using a catch/throw combination if you want to, but since clojure is much more functional than common lisp, the chances you actually need an early return right in the middle of some nested block is much smaller than in CL. The only 'good' reason I can see for return statements is when you're dealing with mutable objects in a way that's not idiomatic in clojure.

I wouldn't go as far as saying that it's never useful, but I think in Clojure, if your algorithm needs a return statement, it's a major code smell.

17
votes

Unless you're writing some really funky code, the only reason you'd ever want to return early is if some condition is met. But since functions always return the result of the last form evaluated, if is already this function — just put the value that you want to return in the body of the if and it will return that value if the condition is met.

15
votes

I'm no expert in Clojure, but it seems it does not have those construct to try to be more functional. Take a look at what Stuart Halloway says here:

Common Lisp also supports a return-from macro to "return" from the middle of a function. This encourages an imperative style of programming, which Clojure discourages.

However, you can solve the same problems in a different way. Here is the return-from example, rewritten in a functional style so that no return-from is needed:

(defn pair-with-product-greater-than [n]
 (take 1 (for [i (range 10) j (range 10) :when (> (* i j) n)] [i j])))

That is, use lazy sequences and returning values based on conditions.

1
votes

As an alternative you could use cond . And if on some conditions you would need to evaluate multiple expressions use do. Here is an example:

(defn fact [x]
  (cond
    (< x 0) (do (println (str x " is negative number"))
                (throw (IllegalArgumentException. "x should be 0 or higher")))
    (<= x 1) 1
    :else (* x (fact (- x 1)))))

0
votes

The if option already given is probably the best choice, and note since maps are easy, you can always return {:error "blah"} in the error condition, and{result: x} in the valid condition.

0
votes

There isn't a return statement in Clojure. Even if you choose not to execute some code using a flow construct such as if or when, the function will always return something, in these cases nil. The only way out is to throw an exception, but even then it will either bubble all the way up and kill your thread, or be caught by a function - which will return a value.

-1
votes

In short, no. If this is a real problem for you then you can get around it with "the maybe monad" Monads have a high intellectual overhead so for many cases clojurians tend to avoid the "if failed return" style of programming.

It helps to break the function up into smaller functions to reduce the friction from this.