Fork me on GitHub

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!
([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.


@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!


also remember that swap! returns the new contents of the atom. Might be helpful.


ins)user=> (def a (atom 0))
(ins)user=> (set-validator! a (fn [x] (even? x)))
(ins)user=> (reset! a 2)
(cmd)user=> (reset! a 3)
Execution error (IllegalStateException) at user/eval146 (REPL:1).
Invalid reference state
(ins)user=> @a
(ins)user=> (try (reset! a 3) (catch IllegalStateException _ {:status 403}))
{:status 403}

πŸ‘ 2

it's much more reliable than trying to join up two atoms


nice. the exception is thrown with the reset!


right, because the validator returned false


lol I should have just done (set-validator! a even?)


Oh, that's great, I will try it out, thanks a lot!


What is the standard way of parameterizing clojurscript builds? I am using Leiningen and I would like for example change some API endpoint or


javascript path based on the build.


if any beginner feels like extending this CLI utility with pretty printing: it might be a fun exercise

Anik Chowhdury14:07:55

Hello everyone, i am new to clojure. I have question regarding CSV. How to append rows in csv in clojure? i have followed


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

πŸ‘ 1
Sam Ferrell15:07:22

any resources/advice for idiomatic argument positions?


^^ This post from Rich Hickey might be helpful!topic/clojure/iyyNyWs53dc

πŸ‘ 1

I generally order from least to most likely to change

Anik Chowhdury16:07:11

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

πŸ‘ 1

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


( s is clojure.spec.alpha btw πŸ˜„ )


use (s/conform (s/* (s/spec ::record)) [[1 "a" "b"] [2 "c" "d"]])


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)


That's exactly the answer I needed πŸ™‚ many thanks


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/*


Yes, perfect πŸ™‚ thanks again


fyi, there is #clojure-spec which is better for these kinds of questions in the future


Oh, ok sorry


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


Well that worked, threw a socketexception but worked


Usually running (exit) or (quit) closes the repl


nREPL server started on port 54179 on host - 
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


Ah, maybe that's a leiningen thing?


it's REPL-y, the lib lein uses for repl client


oh makes sense


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

πŸ‘ 1

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

Janne Sauvala17:07:37

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

Janne Sauvala18:07:24

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


is user.clj an actual file and which the user can override, or is it just a convention?


sorry if this is documented anywhere, i didn't see reference to it on, aside from the recent bug fix


yeah - many suggest not using it, but if it is found, the first user.clj found is used


is it basically similar to how project.clj's :injections behaves?


pretty much, except it's a whole file and not just one clause in a file, yeah


ok. thanks!


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

Sam Ferrell19:07:57

Any resources on using #?(:cljs ...)?

Sam Ferrell19:07:22

or terms I should be searching for?


the keyword to look for is cljc - it only works in cljc files