I'm very much a fan of bailing out early in imperative programming as it helps to avoid a bunch of nested if conditions that are to follow and read. This typically comes up when checking arguments to ensure that they're valid (in range, of acceptable values, etc). One of the major stumbling blocks I've had when writing Clojure is to find a good way to keep code concise, but readable.
For instance, take a snippet like this: def verify_position(pos, type): # It's acceptable to have a None value--it just means don't # change the position for the axis. if pos is None: return True # Anything outside our limits is invalid. if (pos > 90) or (pos < 0): return False if type == 'switched' and pos not in (0, 90): # Switched axes only allow 0 and 90, and nothing in # between. return False if type == 'dual': # We can't control the value on this axis, so anything # other than None is invalid. return False return True I find this very readable in that along the way, I can start throwing things off the raft: after the first condition, I don't need to worry about None being present. After the second, I know the value is within limits, etc. I have a hard time translating the above into (what I believe) is readable Clojure. Here's my stab at it: (defn verify-pos [pos axis-type] (if (nil? pos) ;; nil means don't move the axis. true (case axis-type ;; Only 0 and 90 are allowed on a switched axis. "switched" (or (= pos 0) (= pos 90)) ;; Any non-nil value is disallowed on dual. "dual" false ;; Otherwise, make sure we're within a valid range. (and (<= pos 90) (>= pos 0))))) Now, this was a rather simple example, but you can see some of the nesting starting. Add in another condition like nil?--something that is somewhat global across the different types--and you get another level of nesting in there. I can break it up more: (defn verify-range [pos axis-type] (case axis-type ;; Only 0 and 90 are allowed on a switched axis. "switched" (or (= pos 0) (= pos 90)) ;; Any non-nil value is disallowed on dual. "dual" false ;; Otherwise, make sure we're within a valid range. (and (<= pos 90) (>= pos 0)))) (defn verify-pos [pos axis-type] (or (nil? pos) (verify-range pos axis-type))) And this is a bit better, but you run up against another issue: coming up with good names for each part of the whole so that you can combine them. And, coming up with names that don't sound so similar that folks have to dig into the implementation to know which one is the correct one (I feel the above break up has this problem). In some cases, the error checking logic is really complicated--because the thing being controlled has complex restrictions that are out of my control--and the nesting of if conditionals is maddening. Having to come up with names for each of them would be more than twice as frustrating, as the individual bits don't lend themselves to good names. Then there's the issue of combining the verification and the actual work into a useful function, where you need to verify and then act on it: (defn set-position [pos type] (if (verify-pos pos type) (send-position-to-equip pos) (do-something-for-invalid-input))) Again, this is a simple example, but more complicated cases have more to check, and therefore more nesting of if statements, where the early bail technique leaves the flow pretty readable. I also realize pre- and post- conditions might be useful, but I don't feel they're appropriate when validating user input--especially not assertion errors, which we generally think of programming errors, not user input errors. I realize some of this may be me and shaping my mind more to functional programming, though I do feel like I have a decent grasp of it. I've been dabbling in Clojure for several years, and have more recently incorporated ClojureScript into a project for our web front-end. However, I feel like I might be missing an important strategy for this kind of problem, and I'm hoping that one of you can point me in a good direction. And please don't get hung up on this example. I was trying to boil something much more difficult down into something reasonable that would help show the issue. Thank you! -John -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.