This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-12-15
Channels
- # adventofcode (110)
- # announcements (30)
- # aws (2)
- # babashka (39)
- # babashka-sci-dev (112)
- # beginners (155)
- # calva (5)
- # cider (12)
- # clj-kondo (11)
- # cljs-dev (1)
- # cljsrn (2)
- # clojure (144)
- # clojure-australia (2)
- # clojure-europe (14)
- # clojure-nl (5)
- # clojure-spec (3)
- # clojure-uk (2)
- # clojurescript (22)
- # core-async (23)
- # cursive (31)
- # data-science (3)
- # emacs (12)
- # events (1)
- # fulcro (8)
- # honeysql (7)
- # jobs-discuss (11)
- # lsp (1)
- # missionary (28)
- # nextjournal (7)
- # off-topic (64)
- # pedestal (3)
- # polylith (19)
- # reagent (14)
- # reitit (12)
- # releases (4)
- # shadow-cljs (33)
- # tools-deps (3)
- # xtdb (3)
is there a function like fnil
that checks the return for nil instead of the arg?
(defn f-or [f default]
(fn [& args]
(or (apply f args) default)))
Is their a way to update a key in a map using update
if it doesn't exist. e.g. this (update {:x 0} :y inc)
would result in {:x 0 :y 1}
I thought fnil could maybe help me here, but I can't figure it out. I thought fnil
on the key to return 0 instead of nil? (update {:x 0} (fnil :y 0) inc)
but that still NullPointerException
I presume from inc trying to inc nil
ah, so instead of inc trying to inc nil, it gets passed 0 instead of nil. Ok, that makes sense thanks!
Is there a Clojure equivalent to Python's NotImplementedError
? Just an exception to raise in stub functions that don't have an implementation yet.
UnsupportedOperationException ?
just posting this here because i was unfamiliar with the terms: https://devblogs.microsoft.com/python/idiomatic-python-eafp-versus-lbyl/
exceptions are the reality of the platform, but clojure programs don't use exceptions as promiscuously as python
catching exceptions "at the edge" (I/O generally) and turning that into information (aka maps) is a good strategy
python iterators donβt check some notion of hasNext
but instead catch an exception right? Exceptions are used for control flow if i remember correctly
@dpsutton yes, that's right: https://docs.python.org/3/library/exceptions.html#StopIteration
it is idiomatic in Clojure to raise exceptions for exceptional (unexpected) situations and to NOT raise them for control flow or when you have an invalid input in the range of expected inputs
So if a function has some range of expected inputs but receives an argument that's out of range, what is the strategy to take if the function cannot logically complete? (get [0 1] 2) => nil
but it's a simple case.
@alex.sheluchin It depends what you mean by "out of range". You show get
on a vector (which folks don't often use) but get
returns nil
in a lot of cases that surprise beginners -- and it is designed to allow nil-punning (which is idiomatic in Clojure).
dev=> (get [0 1] 2)
nil
dev=> (nth [0 1] 2)
Execution error (IndexOutOfBoundsException) at dev/eval36734 (dev.clj:1).
in short, it depends
if you're returning a primitive (long or double) that affects your choices
If the function cannot process the inputs and cannot return a "sensible" result in that case, an exception makes more sense.
if you're returning a coll or seq or other thing that is commonly used with nil punning, that affects it, etc
there is of course a wide range of gray area here :)
It Depends(β’) π
Clojure (aggressively) supports polymorphic operations on nil in the core, so it often makes sense in that context to return nil (which allows you to nil pun or chain with other such ops)
lol I take "it depends" as an indicator that I'm starting to ask the right questions π
In JVM-based languages, there are also two very different "types" of exception: β’ An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. β’ The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch.
it means you're out of the range of "idiom" and into "design" :)
I don't actually think the Error/Exception split is that important personally
It's less important in Clojure, for sure.
they both mean something unexpected happened but convey a very coarse level of severity. there are more interesting differences to convey (like with cognitect anomalies)
Yes... I was just about to link to that library! π
I really like the categories there and throwing ex-info
with an anomaly in it works really well.
I think the Exoscale folks have a variant of that as well...?
I'm actually not too familiar with the term "nil punning" and what it means. I get it from the description https://lispcast.com/what-is-nil-punning/, but I'll have to read the rest to understand it better. I guess it's just the falsiness of nil?
essentially, yes
for example, with the new parse-long
etc functions being added to core in 1.11, the expected range of inputs is "a string". So your cases are:
β’ valid string => long value
β’ invalid string => nil (here we opted for a sentinel value indicating invalidity, in particular the value nil which is useful for nil punning)
β’ not a string => throws
(if-let [port (parse-long port-str)] port (report-bad port-str))
Hey, looking at cljs-ajax (https://github.com/JulianBirch/cljs-ajax), and I'm wondering what the best way to log responses is. When I callΒ `(println requrest)`Β , for theΒ `:body`Β section I get something like:Β `#object[org.eclipse.jetty.server.HttpInput 0x186d26d3 org.eclipse.jetty.server.HttpInput@186d26d3]`.
https://github.com/ring-clojure/ring/blob/master/SPEC#L104-L106 the ring spec says the body is an IntputStream
Interesting design there @alexmiller. Two types of invalids... :thinking_face: I see what you mean by different levels of severity. Thanks for the input on this issue everyone. I think the bottom line answer was "it depends" π but it does shed some light.
and if you google org.eclipse.jetty.server.HttpInput you will see that it is an InputStream
https://archive.eclipse.org/jetty/9.2.20.v20161216/apidocs/org/eclipse/jetty/server/HttpInput.html
@hiredman Sure. But I don't know the best way to print out the contents of an InputStream buffer in clojure.
and inputstreams (with some exceptions) are not rewindable, they don't keep track of what has been read, and you can't read from it again
so something like slurp will turn it into a string, but there are consequences to that
Or is data silently dropped if there isn't something to read it at the exact moment data comes in?
Right, the InputStream is server side, but its connected to a browser on the other end, no?
there are buffers, on the browser side and on the server side, potentially lots of them, but that is kind of below the level of abstraction provided in ring there
you get an inputstream, and you can read from an inputstream, and that is a side effecting operation, and once you've read from the it you cannot read again (except some streams support mark and some don't, but that isn't reflected in the class hierarchy because http://java.io's class hierarchy is a mess)
jetty, what most people default to using when making ring apps, runs requests on a threadpool
ring is just kind of an interface definition, it defines how an http request and response can be represented as maps being passed to functions
Specifically this file: https://github.com/JulianBirch/cljs-ajax/blob/master/dev/user.clj
if you are coming from some other runtime (the browser, ruby, python, etc) it can be surprising how cavalier people often are with threads
On line 64 (https://github.com/JulianBirch/cljs-ajax/blob/master/dev/user.clj#L64), I (println request)
> if you are coming from some other runtime (the browser, ruby, python, etc) it can be surprising how cavalier people often are with threads As in threads with clojure, or threads in those other runtimes?
I mean, I'm fine with threads, just worried that concurrency is very tricky to get write.
Although I guess if I'm just logging then inter-thread communication doesn't matter all that much.
noticing this thread here - i think this will do it for you
(require '[clojure.pprint :as pprint])
(defn debug-log-request [request]
(let [body-str (slurp (:body request))]
(pprint/pprint (assoc request :body body-str))
(assoc request
:body
(ByteArrayInputStream.
(.getBytes body-str
StandardCharsets/UTF_8)))))
(defn handler
[request]
(let [request (debug-log-request request)]
...))
hello, me again π I'm wondering if :require is a function, or macro or keyword?
i don't think i understand the utility of keyword very well
:require
is a keyword. I suspect you're referring to its use in the ns
macro? That macro uses several keywords to signify operations that need to be called.
There's a function clojure.core/require
which, ultimately, the ns
macro will expand into calls of.
yes, I am refering to ns macro
ns
is probably the most complicated piece of "syntax" that beginners have to learn.
the fact it's a keyword makes sense but, i don't understand why is it the first item in list then (:require ...)
usually first item is a function
I'm referring to this
dev=> (macroexpand '(ns foo.bar (:require [clojure.string :as str])))
(do
(clojure.core/in-ns (quote foo.bar))
(clojure.core/with-loading-context
(clojure.core/refer (quote clojure.core))
(clojure.core/require (quote [clojure.string :as str])))
(if (.equals (quote foo.bar) (quote clojure.core))
nil
(do
(clojure.core/dosync (clojure.core/commute (clojure.core/deref (var clojure.core/*loaded-libs*)) clojure.core/conj (quote foo.bar)))
nil)))
i'm specifically wondering about the :
part
Keywords always start with :
i assume :
makes something a keyword instead of symbol, is that correct?
:foo
is a keyword and it evaluates to itself.
foo
is a symbol and it evaluates to whatever it is bound to.
alright, so when i'm making an expression with parentheses, the first item in list can be either a keyword or a function?
or can it even be anything?
That's a slightly complicated question to answer.
Inside ns
those are not function calls.
sorry, list
macros can rewrite the code however they want, and the normal rules of evaluation don't apply
but the lists there are being evaluated? specifically here
since they aren't '()
(ns ...)
is a "function call" to the ns
macro and all the rest are arguments. Macros do not evaluate their arguments so ns
is invoked with a bunch of lists -- symbolic expressions.
Functions evaluate their arguments. Macros do not.
and ns is a macro?
okay, that clarifies a ton
those are passed to the ns macro, unevaluated, and the ns macro does whatever it wants with them, and returns some code that is compiled in place of the call to the macro
Yes. A very complex macro.
thank you both very much π
that clarifies a lot
See the macroexpand
call I posted above.
i'm afraid I'm not that advanced to be able to understand it
That shows how ns
expands to calls to in-ns
, refer
, and require
amongst other things.
yea, the moment i was told the list isn't being evaluated, it made all the sense
that was the part that was bothering me
A simpler example of a macro and its expansion:
dev=> (macroexpand '(when (zero? x) (println x "is zero!")))
(if (zero? x) (do (println x "is zero!")))
yea, the println list was not evaluated
it was only being passed through
makes all the sense
so when you write (when cond expr1 expr2 expr3)
it is expanded to (if cond (do expr1 expr2 expr3))
and then that is evaluated.
totally
again, thanks to both