Fork me on GitHub
#beginners
<
2018-01-10
>
zsck00:01:24

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?

noisesmith00:01:26

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

seancorfield00:01:47

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.

jgh00:01:42

i kinda hate exceptions

seancorfield00:01:56

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.

jgh00:01:13

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

seancorfield00:01:13

(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)

noisesmith00:01:22

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

seancorfield00:01:38

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

...it would be nice to have catch extended to hide that sort of boilerplate.

zsck00:01:19

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.

zsck00:01:31

Probably just being mentioned for completeness.

seancorfield00:01:55

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

jgh00:01:42

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

jgh00:01:46

cause that is completely expected 😛

jgh00:01:22

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

seancorfield00:01:22

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

seancorfield00:01:26

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.

seancorfield00:01:44

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

seancorfield00:01:10

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

jgh00:01:56

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

noisesmith00:01:22

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)

jgh00:01:24

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

noisesmith00:01:02

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

jgh00:01:07

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

jgh00:01:45

yeah could be

zsck00:01:06

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.

noisesmith00:01:09

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”

jgh00:01:35

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"

noisesmith00:01:01

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

noisesmith00:01:10

because we both know java is not going to change

jgh00:01:16

yeah i know 😛

jgh00:01:25

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

seancorfield00:01:44

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 🙂

noisesmith00:01:20

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

noisesmith00:01:34

maybe some other naming that is less punnish

seancorfield00:01:35

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...

jgh00:01:26

haha oh boy

nulligor02:01:20

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!

nulligor02:01:05

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 []))))

dpsutton02:01:18

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

dpsutton02:01:32

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

jgh02:01:15

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

lepistane11:01:28

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-java

danlebrero14:01:42

Why does iterate require a pure function? https://clojuredocs.org/clojure.core/iterate

mdrago102614:01:11

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

danlebrero14:01:52

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

mdrago102614:01:02

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

danlebrero14:01:23

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

danlebrero14:01:56

could somebody confirm/deny?

seancorfield17:01:18

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

seancorfield17:01:11

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

alexmiller17: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.

alexmiller17:01:43

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

alexmiller17:01:05

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

alexmiller17:01:57

such that repeatedly explicitly expects a function with side effects

seancorfield17:01:05

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

alexmiller17:01:27

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

seancorfield17:01:45

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.

lilactown18:01:29

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

lilactown18:01:44

(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)})))

noisesmith18:01:59

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

lilactown19:01:32

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

lilactown19:01:22

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

lilactown19:01:25

ah I think I can just use js-obj

lilactown19:01:54

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#)))))))

lilactown19:01:16

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]]))

lilactown19:01:35

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.

lilactown19:01:12

do macros have to be defined in .clj files?

lilactown19:01:59

😂 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

lilactown19:01:25

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

lilactown19:01:51

and js-obj does not work

rauh19:01:48

@lilactown use cljs.core/js-obj

rauh19:01:16

ALSO: change (name name) to something 🙂

noisesmith19:01:44

yeah, that shadowing is a huge problem good catch

lilactown19:01:20

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

pablore21:01:24

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

noisesmith22:01:30

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

noisesmith22:01:43

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)

noisesmith22:01:39

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