This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-01-10
Channels
- # adventofcode (3)
- # aws (2)
- # beginners (85)
- # boot (8)
- # boot-dev (4)
- # cider (36)
- # clara (3)
- # cljs-dev (87)
- # cljsrn (3)
- # clojure (87)
- # clojure-austin (12)
- # clojure-brasil (1)
- # clojure-dev (8)
- # clojure-dusseldorf (5)
- # clojure-estonia (5)
- # clojure-greece (4)
- # clojure-italy (3)
- # clojure-spec (17)
- # clojure-uk (55)
- # clojurescript (70)
- # core-logic (2)
- # cursive (6)
- # data-science (18)
- # datomic (13)
- # emacs (34)
- # fulcro (347)
- # graphql (12)
- # hoplon (6)
- # jobs (3)
- # jobs-discuss (43)
- # juxt (2)
- # keechma (31)
- # leiningen (29)
- # lumo (2)
- # midje (2)
- # off-topic (118)
- # om-next (4)
- # onyx (39)
- # pedestal (6)
- # re-frame (85)
- # reagent (21)
- # remote-jobs (3)
- # ring (5)
- # rum (2)
- # shadow-cljs (126)
- # spacemacs (1)
- # sql (6)
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'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 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...
...it 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.
They're acceptable if the errors are unexpected and indicate things you can't handle at your level.
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)
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 😛 )
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
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
🙂
indeed!
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 Error
s and clojure.spec/assert
that throws clojure.lang.ExceptionInfo
which is an Exception
, not an Error
just to muddy the water...
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
3
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")
(clojure.string/split-lines))))
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-lines)
(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]
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
https://stackoverflow.com/questions/12771737/conversion-string-to-uuid-in-postgres-and-javaWhy does iterate require a pure function? https://clojuredocs.org/clojure.core/iterate
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.
thanks a lot @seancorfield
It would be a fun exercise to construct a test to prove that 😃
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.
however, the implementation is from clojure 1.7.0 and the docstring constraint predates that implementation
I think the note in the docstring here is in some sense to contrast iterate
with repeatedly
such that repeatedly
explicitly expects a function with side effects
wow! thanks @alexmiller
Good point about using the result of iterate
in both a seq and a reduce context recomputing values -- I hadn't thought about that.
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.
(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
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
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.
😂 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
@lilactown use cljs.core/js-obj
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