Fork me on GitHub

I'm used to the idea of using exceptions being considered a mostly bad idea if you want easy to reason about code in an OO/imperative language. I'm curious though; in the case of a functional language like Clojure, is the use of exceptions actually less problematic? Is it still preferable to do error handling another way?


often it’s useful to use a value that represent an unexpected outcome, then you can process that functionally. And there are a few monadic or otherwise functional libraries that replace exceptions with things like conditions. But the thing is that due to the fact that we are on the jvm we also need to handle exceptions as well, so anything nicer we use can’t replace exception handling, but becomes a parallel system


I think it's generally good practice -- in both OOP and FP -- to use exceptions only for exceptional situations rather than "errors" that you expect to occur and know how to handle.


i kinda hate exceptions


I've seen a lot of code out there that uses exceptions as a way convey "values" across multiple call frame boundaries and that's bad, regardless of the language.


i think objective-c got it right with a robust error object


(I think ex-info actually encourages that bad value-conveyance practice but it was added specifically to allow values to be thrown and caught elsewhere so shrug)


it’s better then expecting people to subclass Exception or Throwable though


Aye, true. Exceptions-as-data makes sense in Clojure but you can't catch different "types" of ex-info -- you can only blanket catch clojure.lang.ExceptionInfo and then inspect ex-data and figure out what to do...

seancorfield00:01:13 would be nice to have catch extended to hide that sort of boilerplate.


I'm glad to hear there's a preference not to use exceptions in Clojure. I'm not a fan of them myself. I was just watching a talk about APIs that suggested using exceptions may be an acceptable way to handle errors in a library.


Probably just being mentioned for completeness.


They're acceptable if the errors are unexpected and indicate things you can't handle at your level.


unexpected probably shouldnt include things like “ermahgerd this file didnt parse”


cause that is completely expected 😛


and yet there is a popular c++ json lib that uses exceptions in that case 😛


I like clj-http's approach: you can tell it whether to throw exceptions or not for non-200 OK responses.


Pretty sure the popular Clojure JSON libs all throw exceptions for parse failures... And that's actually fine: the library can't handle that -- it has no idea what to do about a failure to parse data and it might be able to include some useful debugging information in the exception it throws.


The caller can/should decide what to do about a JSON parsing failure.


After all, it might be a fatal error for some apps or a recoverable error for others.


i guess it’s about perspective of degrees of exceptionality. I think that a parse error is completely normal for a parser to encounter, so it should be handled with a regular ol’ error. I agree with Apple’s view (from ObjC) that an exception should only be used in the gnarliest of situations


the terminology gets mucked up here because there’s also a thing called Error in the jvm, and the guideline is that if Error is thrown, catching it can be a last ditch attempt to clean up before the vm dies but you shouldn’t try to resume normal execution, while Exception means you can do as you like (eg. skip this request and process the next one for a server)


however, exceptions do provide a relatively standard mechanism for propagating errors.


which makes me think that the jvm authors are using exception / error in exactly the opposite way you are


(i say relatively because you still need to figure out what exceptions they might actually throw 😛 )


yeah could be


Well, a big part of the problem with using exceptions too much was that, depending on how rigorously you do or don't handle them, they can lead to unexpected jumps around your codebase, resulting in difficult-to-comprehend control flows. In imperative languages, this is very problematic because state is suddenly going to be mutated out of order. It does seem like it could be handled more effectively with values in Clojure as well, though.


exception: “I couldn’t parse this data, what now?” error: “the vm is in an invalid state, this is your chance to gracefully shut down before we are killed”


i prefer error: "I couldnt parse this thing", exception: "got myself into a situation with this data.", fault: "the kernel is telling me i just shat the bed"


@jgh but if I accept your definitions I now need to use two opposite definitions


because we both know java is not going to change


yeah i know 😛


people should just ask me from the start, it would make things easier 😉


It also highlights that if we have :pre on a function it should be for "this should never happen" bad arguments vs manual argument checking that might throw IllegalArgumentException 🙂


it would be cool to have support for :pre and :post extended to eg. :prefer and :postfer with IllegalArgumentException instead of AssertionError


maybe some other naming that is less punnish


And we have assert that throws Errors and clojure.spec/assert that throws clojure.lang.ExceptionInfo which is an Exception, not an Error just to muddy the water...


haha oh boy


hey all, it's my first day using clojure and I have a text file with line breaks and whitespaces separating some numbers like:

1 2
and I was trying to cast into a vector like [[1 2] [3]] by splitting twice (whitespaces and line breaks), I came up with
(conj [] (-> (slurp "path/to/file.txt")
but this only gives me [[1 2 3]]. any help would be much appreciated, ty!


I also tried

(-> (slurp "path/to/file.txt")
      (clojure.string/split #" "))
but it blows up saying a persistent vector cannot be cast into a char sequence EDIT: managed to make it work with
(defn read-input [path]
  (with-open [rdr ( path)]
      (->>  (line-seq rdr)
            (map #(clojure.string/split-lines %)) 
            (map #(map (fn [s]
              (map read-string (clojure.string/split s #" ")))
            (map #(flatten %)) 
            (into []))))


Make smaller composable steps so that you can see what's going on. Make a function that turns "1 2" into [1 2]


And then work about applying that to each line of the file


i dont understand how you’re getting [[1 2 3]] out of that operation and not [["1 2" "3"]]


hello guys i am using clj-jdbc for the first time (i was using libs like honeysql or hugsql) i am trying to do the simple select

(defn has-details? [db client-id]
  (j/query db ["select * from clients_details where client_id = ?" client-id]))
client-id is UUID and i am getting this error
Unhandled org.postgresql.util.PSQLException
   ERROR: operator does not exist: uuid = character varying Hint: No
   operator matches the given name and argument type(s). You might
   need to add explicit type casts.  Position: 47
what can i do? i was reading docs for query but couldn't find anythign? edit: SOLUTION


Why does iterate require a pure function?


i think because it’s lazy so those side effects won’t be realized unless your force it


@mdrago1026 map is also lazy and it doesnt say anything about pure fn


good point. wonder if it is just a difference in the doc wording


Looking at the implementation of iterate, I think it is because if there are multiple threads pulling values, it is possible that f will be called more than once for the “next” value


could somebody confirm/deny?


@danlebrero I think you're right. Neither first nor next are thread-safe in terms of calling f only once.


It would be a fun exercise to construct a test to prove that 😃

Alex Miller (Clojure team)17:01:18

the current implementation of iterate relies on the presumption that f is pure and may be invoked more than once. it relies on that both in the way you state and also in that the reduce path does not cache its values so if you use the result of iterate in both a seq and a reduce context, values may be recomputed.

Alex Miller (Clojure team)17:01:43

however, the implementation is from clojure 1.7.0 and the docstring constraint predates that implementation

Alex Miller (Clojure team)17:01:05

I think the note in the docstring here is in some sense to contrast iterate with repeatedly

Alex Miller (Clojure team)17:01:57

such that repeatedly explicitly expects a function with side effects


Good point about using the result of iterate in both a seq and a reduce context recomputing values -- I hadn't thought about that.

Alex Miller (Clojure team)17:01:27

the only reason we can do so is because of the constraint in the docstring


In general, I'd say it's good practice to separate out side effects and work with pure functions as much as possible. repeatedly, doseq, run!, and a handful of others are specifically designed for side-effects. Makes code easier to reason about, easier to test, easier to reuse.


I’m having an issue defining a macro in cljs that should output a #js literal


(defmacro gen-lambda [{:keys [name handler simple]}]
  (if (false? simple)
    `(set! (.-exports js/module) #js {~name ~handler})
    `(set! (.-exports js/module) #js {~name (handle-request ~handler)})))


creating a reader macro from a macro is weird, I’m not certain it’s even possible


:thinking_face: what would be the best way to do this, then?


what I’m looking for is a way to pass a map with a name and function, and it will generate the node.js export for me


ah I think I can just use js-obj


ok… now I’m really confused. I have a file called lambda.cljs with this in it:

(ns cljs-dashboard.lambda)

(defmacro gen-lambda [{:keys [name handler simple]}]
  (if (false? simple)
    `(set! (.-exports js/module) (js-obj ~(name name) ~handler))
    `(set! (.-exports js/module)
           (js-obj ~(name name)
                   (fn [event# ctx# cb#]
                     (cb# (~handler event# ctx#)))))))


in core, I’m importing it like so:

(ns cljs-dashboard.core
  (:require [cljs.nodejs :as nodejs]
            [reagent.dom.server :as r]
            [cljs-dashboard.lambda :refer-macros [gen-lambda]]))


when I try to build, I get the error:

Could not locate cljs_dashboard/lambda__init.class or cljs
_dashboard/lambda.clj on classpath. Please check that namespaces with dashes use underscores in the 
Clojure file name.


do macros have to be defined in .clj files?


😂 renaming it to lambda.clj then gives me the error

No such namespace: cljs-dashboard.lambda, could not locate cl
js_dashboard/lambda.cljs, cljs_dashboard/lambda.cljc, or JavaScript source providing "cljs-dashboard
.lambda" in file src/cljs_dashboard/core.cljs


OK, renaming it to lambda.clj and changing the import to use (:require-macros) solved it


and js-obj does not work


@lilactown use cljs.core/js-obj


ALSO: change (name name) to something 🙂


yeah, that shadowing is a huge problem good catch


it’s working! Thank you @noisesmith @rauh 🙂


how do you guys keep your source code below 80 characteres per line without losing readability?


not using nested expressions on a single line, and avoiding long names


I suspect sometimes that people heard someone complain about line count (or praise code for a low line count) and then decided it was a good idea to put as much logic on one line as possible (and leave expressions nested as much as possible)


even where it’s better to leave an expression nested, I still want to separate things so there’s one thing being done on each line as much as possible