This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-07-19
Channels
- # aleph (1)
- # announcements (3)
- # aws (1)
- # beginners (95)
- # calva (15)
- # clojars (4)
- # clojure (84)
- # clojure-android (3)
- # clojure-austin (1)
- # clojure-chicago (1)
- # clojure-dev (66)
- # clojure-europe (3)
- # clojure-italy (15)
- # clojure-nl (13)
- # clojure-uk (33)
- # clojuredesign-podcast (9)
- # clojurescript (6)
- # cursive (68)
- # data-science (4)
- # datavis (1)
- # datomic (13)
- # emacs (4)
- # fulcro (2)
- # jobs (4)
- # jobs-discuss (89)
- # luminus (23)
- # nrepl (6)
- # off-topic (2)
- # onyx (2)
- # pathom (4)
- # pedestal (11)
- # re-frame (9)
- # reagent (8)
- # reitit (5)
- # shadow-cljs (131)
- # spacemacs (13)
- # sql (8)
- # vim (8)
- # xtdb (7)
- # yada (4)
Another doubt! I'm trying to change an atom that represents my state, but I need to make sure that the new state is "valid". So far I'm doing it with a simple check before calling swap!
, but this obviously is vulnerable to race conditions. I know the alternative is to attach a validator for this atom, the problem is that if the validators fails, the program will throw an exception, and I actually want to leave the state the way it was, and return a response with 403 status code (it's a REST API). Is there something like a try-catch
thing that I could use? or what would be the clojure-way to do this right?
one possibility that comes to mind -- you could perform the validation check inside the function that you pass to swap!
, with a try
/ catch
inside in case any exceptions occur, and make that function return the current value if anything goes wrong.
you would need some way to record the fact that a problem occurred while that function was called, which I don't have any great suggestions for
perhaps error conditions could be stored in a second atom, one dedicated solely for the purpose of recording errors that occur in that updater function.
Great, that's an idea, would it be fine in this case to violate the purity of the function passed to swap!
in this case? since I will be modifying another atom. Hmm... now that I think more about it, it might be difficult to know if the error came from the current thread's call to swap!
or from some other.
You can't synchronise across atoms. In these cases, you either want one atom (so both your processing/processed state, and the error condition is inside the same atom), or you use refs, which can be synchronised across refs.
its a bad idea to modify one atom inside another atoms function. Remember those functions can be run multiple times by the STM for a single CAS operation.
If err-atom
is an atom that only ever has calls made to update it, from the update function of data-atom
, and nowhere else, then I think you can guarantee their updates are synchronized. Not for general sets of atoms, but in that special case, perhaps?
@maxiredigonda there's a built in function: clojure.core/set-validator!
user=> (doc set-validator!
)
-------------------------
clojure.core/set-validator!
([iref validator-fn])
Sets the validator-fn for a var/ref/agent/atom. validator-fn must be nil or a
side-effect-free fn of one argument, which will be passed the intended
new state on any state change. If the new state is unacceptable, the
validator-fn should return false or throw an exception. If the current state (root
value if var) is not acceptable to the new validator, an exception
will be thrown and the validator will not be changed.
nil
@noisesmith yes! I knew about this function but sadly it doesn't solve my problem directly, since it throws an exception instead of politely inform me that the swap has failed so I can return that to the user in the form of a 403 http response.
that's just a question of catching an exception and returning 403 from the catch
Combining @crispin and @andy.fingerhut ideas I might be able to make it work now, there is just one small thing that I'm left to figure out now, but I think I'll ask about it later if it becomes a problem, thank you all guys!
ins)user=> (def a (atom 0))
#'user/a
(ins)user=> (set-validator! a (fn [x] (even? x)))
nil
(ins)user=> (reset! a 2)
2
(cmd)user=> (reset! a 3)
Execution error (IllegalStateException) at user/eval146 (REPL:1).
Invalid reference state
(ins)user=> @a
2
(ins)user=> (try (reset! a 3) (catch IllegalStateException _ {:status 403}))
{:status 403}
it's much more reliable than trying to join up two atoms
right, because the validator returned false
lol I should have just done (set-validator! a even?)
What is the standard way of parameterizing clojurscript builds? I am using Leiningen and I would like for example change some API endpoint or
@finn.volkel https://clojurescript.org/reference/compiler-options#closure-defines and https://cljs.github.io/api/cljs.core/goog-define
if any beginner feels like extending this CLI utility with pretty printing: https://github.com/borkdude/jet/issues/4 it might be a fun exercise
Hello everyone, i am new to clojure. I have question regarding CSV. How to append rows in csv in clojure? i have followed https://github.com/clojure/data.csv
append, like add to an existing file?
I think if you just open your writer on an existing file in append mode, it will do that
(with-open [writer (io/writer "existing-file.csv" :append true)]
(csv/write-csv writer
[["abc" "def"]
["ghi" "jkl"]]))
any resources/advice for idiomatic argument positions?
^^ This post from Rich Hickey might be helpful https://groups.google.com/forum/#!topic/clojure/iyyNyWs53dc
I generally order from least to most likely to change
@alexmiller actually i don't want to append data in the end. Rather i want to append data in each row.
in that case, you'll probably need to read the csv, modify the data, then write the csv. data.csv doesn't have any support for modifying an existing csv file like that.
does anyone know what I could use instead of (s/*)
to make this conform pass ?
(s/def ::record
(s/cat :f1 int?
:f2 string?
:f3 string?))
(s/conform (s/* ::record) [[1 "a" "b"] [2 "c" "d"]])
use (s/conform (s/* (s/spec ::record)) [[1 "a" "b"] [2 "c" "d"]])
Thanks a lot @alexmiller
regex ops merge together to describe the same collection, so your spec above is looking for [1 "a" "b" 2 "c" "d"]
you can use s/spec to introduce a spec boundary and describe a nested collection
an alternate solution (and possibly better here) is to use (s/coll-of ::record)
and another is to use s/tuple instead of s/cat - (s/def ::record (s/tuple int? string? string?))
- that will conform to a vector instead of a map. one may be better for you.
The coll-of isn't an option for me unfortunately because the actual vector of records appears in a s/cat
The tuple is what I was using until now but I wanted to add field names in the conform
gotcha, then given all that I'd use s/cat and the s/spec around s/*
fyi, there is #clojure-spec which is better for these kinds of questions in the future
no worries! :)
Hello. I'm using lein and trying to set my repl to always pprint results by default. I tried using
(clojure.main/repl :print pprint)
and while this works, it somewhat messes up my repl. I can't exit after that. Ctrl+D prints ^D
and (exit)
gives "unable to resolve symbol."Any suggestions for making this work? Is it a windows powershell thing that's causing the issue?
try (System/exit 0)
I don't think (exit) ever means anything
weird
nREPL server started on port 54179 on host 127.0.0.1 -
REPL-y 0.4.3, nREPL 0.5.3
Clojure 1.9.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_211-b12
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
user=> (exit)
Bye for now!
that must be a reply thing or something, it's not clojure
which is why running clojure.main/repl means it wouldn't work, I assume
it's REPL-y, the lib lein uses for repl client
(as shown in the splash printout)
it's amusing that none of the three methods they suggest for exiting are actually a thing that clojure itself identifies as a reason to exit
Interesting. I need to venture outside of lein soon. It seems the tooling outside of it is improving? And I don't mean boot, but clojure main itself? I may not know what I'm talking about 😋
clojure has a new dep resolution tool, and people are creating build tools to go with it, since dep resolution is the hard part
it's good to know which tool does which layer of functionality though (even if you use lein, you should only be using it for dev and build for example, it shouldn't run on your server, so it's important to know which things are it and which are clojure itself)
(control-d signals eof, which might mean exit but... as we saw it's possible to create a situation where it doesn't mean that)
Hey 👋:skin-tone-2: I’m creating a server that provides an API. Would you recommend using Ring or Pedestal? I’m planning to add GraphQl support with Lacinia later on
@janne.sauvala I think if you plan to provide lacinia graphql, it's easier ot start that way
I think Lacinia still needs other libraries to create the HTTP endpoints. Lacinia has a supporting library for Pedestal, so maybe I use those two
right - you'd still need to set up ring or pedestal, but the way you define handlers is different with lacinia
i'm using lein repl
and am trying to in-ns
into a test namespace, but the repl isn't seeing it (e.g. using tab completion) for some reason. the test namespace lives under test/
, and my project.clj
does include :test-paths ["test"]
. what do I need to do to get the repl to be aware of this ns?
you need to require the namespace - for tests, they aren't loaded up by default on startup
thanks. is how this works documented anywhere (or do you know which code handles this?). e.g., how does the repl populate this list of available ns-es?
the repl itself only loads user
, leiningen will also load your init-ns
or main-ns
if defined
so i guess the precedence order for where lein repl
dumps you is
1) [:repl-options :init-ns]
2) :main
3) user
yeah - and any other namespaces are loaded if they are transitively pulled in by init-ns, main, or user (via user.clj)
user.clj stuff is pulled in even if you start in another ns - loaded but not neccessarily the the ns you are in
https://github.com/clojure/clojure/blob/d07ef175c700329f7afbef8770332b6247a34a49/src/jvm/clojure/lang/RT.java#L486 Looks like it's loaded if it's in the classpath
sorry if this is documented anywhere, i didn't see reference to it on http://clojure.org, aside from the recent bug fix
yeah - many suggest not using it, but if it is found, the first user.clj found is used
pretty much, except it's a whole file and not just one clause in a file, yeah
also, watch out for calling in-ns
on a ns that hasn't been created yet (eg. via require) - it can land you in a broken namespace (you can get back out via (clojure.core/refer-clojure)
then another in-ns call, or just (clojure.core/in-ns 'working-ns)
)
Any resources on using #?(:cljs ...)
?
or terms I should be searching for?
the keyword to look for is cljc - it only works in cljc files