Fork me on GitHub
Travis Jefferson02:06:58

Whew I’m struggling! I’ve got a tiny clojure program running on a remote server, using deps.edn and (hopefully) clj. It’s a daemon/run-forever type script, and I’m having a lot of trouble figuring out how to “escape” the repl and leave the process running in the background


how are you starting your program?

Travis Jefferson02:06:02

I’ve tried a number of things, but I started here clj -m <ns>

Travis Jefferson02:06:33

I have a -main function in that ns, and it’s called correctly (the code even works!)


/t/run ❯❯❯ cat deps.edn
{:paths ["."]}
/t/run ❯❯❯ cat foo.clj
(ns foo)

(defn -main [] (println "hello and goodbye!"))
/t/run ❯❯❯ clj -m foo
hello and goodbye!
/t/run ❯❯❯


my program doesn't start a repl.

Travis Jefferson02:06:05

indeed! I’ve got an infinite sequence thing going on

Travis Jefferson02:06:11

let me share a snippet

Travis Jefferson02:06:34

(defn start-schedule []
  (-> (chime/periodic-seq (Instant/now) (Duration/ofMinutes (:interval-minutes env)))
      (chime/chime-at (fn [time]
                        (println "Checking for dogs at " time)

(defn -main []


ok. and what is your expectation?

Travis Jefferson02:06:29

I’d like for -main to stay alive and continue to trigger per chime’s schedule

Travis Jefferson02:06:54

but I’d also like to detach from this terminal’s process and kill my ssh connection

Travis Jefferson02:06:12

leaving clojure/java happily churning away on my remote machine


does it currently exit? i'm not familiar with chime

Travis Jefferson02:06:38

it does not, it consumes the shell and leaves me no choice but to CTRL-C it


so your only problem is how to run this process in the background


it correctly "runs forever" already?

Travis Jefferson02:06:41

yes. I’m open to opinionated suggestions! I’m not married to clj -m

Travis Jefferson02:06:45

I’ve tried bash-style backgrounding with & but that does something bad to the input/output streams


do you need input and output streams?

Travis Jefferson02:06:53

no to input. I do have some println statements; I’d like those be available if possible


and you want those to just pop up randomly in a terminal that's doing other things?

Travis Jefferson02:06:40

it’s a remote server, so I suppose ideally they’d go to syslog or redirect to a file or somethin

Travis Jefferson02:06:36

they’re not particularly important overall

Travis Jefferson03:06:36

I think I’m having more luck now

Travis Jefferson03:06:53

I’m running the same command clj -m <ns> but now it’s through systemd

Travis Jefferson03:06:07

that appears to be sufficiently “in the background”


> ... through systemd That's the Right Way 👍

😀 1

there are various tools for this, in reverse order of hackiness: • run a repl inside tmux/screen and detach it from its controlling tty • use nohup to run from the background and log to a file • use jsvc (the unix version of Commons Daemon) a tool that lets a java program act as a proper service in a cross platform way • dedicated service integration tailored to your target OS (eg. systemd)

❤️ 1

I have a shim java project that makes it easy to have jsvc use a specific clojure namespace, without needing AOT or interop in the Clojure code.


it requires you to use a system property to register a clojure namespace containing regular functions to be used for each jsvc lifecycle method


oh and another option is to use Docker plus a microservice distsys app like mesos / k8s


(what my company actually does)

Travis Jefferson15:06:27

cool, thanks for enumerating all those options

Travis Jefferson15:06:38

I was ready to go docker plus some orchestrator

Travis Jefferson15:06:03

but it's just a lil DO droplet, didn't wanna burn any more resources than I already was

Travis Jefferson15:06:25

I really wanted a socket repl server to work

Travis Jefferson15:06:44

but that seems like it doesn't wanna be background-ed either (unless you do something fancy like your tmux suggestion)

Travis Jefferson15:06:45

nohup sounds exactly like what I wanted

Travis Jefferson15:06:02

thanks all for chiming in!


with my lib, jsvc isn't hard to use, and the advantage of jsvc is you get restarting / monitoring / proper system level logging


but if nohup is enough and you don't mind manually checking and restarting, cheers 🍻

Travis Jefferson15:06:32

whale I'm not gonna UNDO my systemd work


socket-repl and nohup are compatible

Travis Jefferson15:06:14

unless you think there's a limitation of systemd I might run into?


oh no, beyond portability concerns (which don't seem like an issue, and you'll know what they are) systemd is fine

Travis Jefferson15:06:06

thanks much @U051SS2EU this was educational 🧠


> * run a repl inside tmux/screen and detach it from its controlling tty > * use nohup to run from the background and log to a file Hasn't there been issues of systemd killing such procs when they lose their tty, and weren't run with systemd-run?


last I used those techniques was pre-systemd honestly


I mean, I'm a professional after all and those are hacky solutions :D


The world runs on hacky solutions 😜


worse is better, but I strive for better than that, to coin a phrase


Indeed. We're here for [a] lisp, after all 🙂


If I have the following map {:players {:player1 {:hand :rock}, :player2 {:hand :rock}}} as input to the following:

(defn determine-winner
  (let [player1-hand (get-in game [:players :player1 :hand])
        player2-hand (get-in game [:players :player2 :hand])]
    (if (= player1-hand player2-hand)
      (if (= (player1-hand dominated-by) player2-hand)


How can I better destructure the input? In other words, do you reckon the let block is fine in this case or is there a more idiomatic way to do this?

seancorfield05:06:37 I'd probably declare the argument to be [{:keys [players]}] and then your let bindings are one level less.


(since you're not using game anywhere else in the function)


@seancorfield Cool! Any way to destructure "deeper" than that even?


There is, but I'm not sure I'd find it easier to read than the code you already have


Nested destructuring can be very hard to read, in my opinion.


Fair enough. Thanks @seancorfield 🙂


Next question. 🙂 I'm learning spec and have the following:

(ns rock-paper-scissors.core
  (:require [clojure.spec.alpha :as s]
            [clojure.spec.gen.alpha :as g]))

(def hand? #{:rock :paper :scissors})

(s/def ::hand hand?)
(s/def ::player (s/keys :req [::hand]))
(s/def ::player1 ::player)
(s/def ::player2 ::player)
(s/def ::players (s/keys :req [::player1 ::player2]))
(s/def ::game (s/keys :req [::players]))

(def dominates
  {:rock     :scissors
   :paper    :rock
   :scissors :paper})

(g/sample (s/gen ::game) 5)


g/sample returns

Class: clojure.lang.LazySeq
  0. { :rock-paper-scissors.core/players { :rock-paper-scissors.core/player1 { :rock-paper-scissors.core/hand :scissors }, :rock-paper-scissors.core/player2 { :rock-paper-scissors.core/hand :scissors } } }
  1. { :rock-paper-scissors.core/players { :rock-paper-scissors.core/player1 { :rock-paper-scissors.core/hand :scissors }, :rock-paper-scissors.core/player2 { :rock-paper-scissors.core/hand :rock } } }
  2. { :rock-paper-scissors.core/players { :rock-paper-scissors.core/player1 { :rock-paper-scissors.core/hand :rock }, :rock-paper-scissors.core/player2 { :rock-paper-scissors.core/hand :rock } } }
  3. { :rock-paper-scissors.core/players { :rock-paper-scissors.core/player1 { :rock-paper-scissors.core/hand :rock }, :rock-paper-scissors.core/player2 { :rock-paper-scissors.core/hand :scissors } } }
  4. { :rock-paper-scissors.core/players { :rock-paper-scissors.core/player1 { :rock-paper-scissors.core/hand :scissors }, :rock-paper-scissors.core/player2 { :rock-paper-scissors.core/hand :scissors } } }


How do i extract the data from that as an actual map?


g/sample produces a sequence of maps. I'm not sure what you're asking.


Sorry, to be more clear, what I'm asking if those sequence of maps can return {:players {:player1 {:hand :scissors}, :player2 {:hand :scissors}}}`` for item 0 above. Does that make sense?


No, I don't understand your question.


Your specs use qualified keywords. What did you expect to get from g/sample?


The output above is correct.


(I don't know what you're using to run the code -- a regular REPL produces much nicer output than that)


Ah wait, I think I understand. I didn't realize :rock-paper-scissors.core/players was an actual keyword. I was expecting just :players. But I get it now.


Thanks for the help @seancorfield!


In a normal REPL those maps would be a lot easier to read


Hmm, maybe not because they are nested. Anyways, they are exactly what I'd expect to get -- qualified keywords.

Endre Bakken Stovner09:06:40

When running a program with lein run :hi "there" is there a way to tell the main function to get the args as a dict? Or do I have to write that code myself? '(":hi" "there") My current code for turning the args into a dict is

(into {} (for [[k v] (partition 2 args)] [(symbol k) v])
dunno if there is a better way to do it :)


I think you have to parse it - something like:

lein run '{:a 1 :b 2}'

;; the in code
(clojure.edn/read-string arg)


I'm not saying it's better, but here's another way:

(->> '(":hi" "there" ":foo" "bar")
     (partition 2)
     (map (juxt (comp symbol first) second))
     (into {}))
;;=> {:hi "there", :foo "bar"}

Endre Bakken Stovner10:06:45

Thanks both of you 🙂

Ben Sless10:06:09

You can, but why not use a library for it?

👍 1
Endre Bakken Stovner11:06:03

Does tools.cli work easily with lein run?

Ben Sless17:06:10

Never tried, I usually run my stuff as uberjar in the end.

Endre Bakken Stovner11:06:07

I have code like this:

(def rules (atom {}))

(defmacro defrule
  "TODO: test me."
  [name & body]
     (let ["bla" "bla]
       (swap! rules assoc kw-name# (handle-docs body#)))))
What the code does is allow users to define rules. With the macro, they are all collected into the atom rules. Is there a way to do this in a stateless way - i.e. without the atom?


If they are dynamically(during runtime) changing the rules i dont think there is stateless way. However if the rules are only initialized at startup or obtained somewhere but without dependency on a particular point in time(like every 5 minutes?) - I recommend memoize


Yeah. State requires... well... statefulness.


Another way that you can store that state, is as metadata in def/`defn` statements that you output from that macro. You can then iterate over the symbols in the ns and filter for the metadata.


(I saw some lib doing this, but I can't recall which one.)


I just happened to find it: Metabase's defendpoint generates compojure routes, but with :is-endpoint? true in the metadata

Endre Bakken Stovner10:06:30

Thanks mm and walterl. @UJY23QLS1 I think clara-rules also stores its rules with meta-data. I do not see the advantage over just having an atom though. See


The difference, I think, is that metadata is "more static" than using an atom, and I would suggest using what fits best. If the macro string metadata (conceptual, rather than Clojure-specific), store it in (Clojure) metadata, if it's collecting data you're working with, an atom is probably more suitable.

👍 1

I think what bothers me about hard-coding an atom in a macro like that, is that it makes "magic" out of the data collection being performed by the macro, by not referencing the atom in its usage. I.e. when reading a (defrule ...) usage, there is no hint that the rules atom is being used.

👍 1
Endre Bakken Stovner10:06:04

Good points. I will collect metadata instead


Hi, I am currently refactoring some of clojure codebase, one of the easy targets for me is to provide a way to access same config in different flavours in lein. I saw that you can use :profile in project.clj. I want to have 2 configs: :test and :dev how would I go about that in code, so when the lein test is called it loads a different file from profile.


thats great, thanks - how do I later access it?


Aliases like those are used directly with lein. So you just run lein test, and it's "expanded" (internally) to lein with-profile +test test


that's fine - but how do i later access that variable?

:profiles {:dev      {:env {:config-path "conf/app.conf"}
           :test     {:env {:config-path "conf/app.test.conf"}}
I presume it will be available in env?


[environ "0.5.0"]


@mm lein with-profile +test test


could you elaborate 🙂?


with-profile +test runs your tests with test profile. with-profile +abc runs your tests with abc profile


Using +abc adds the abc profile to the default ones, right? If you run it without the plus sign, than it effectively replaces the profiles.


I believe it overrides it unless the explicitly specified profile extends another one


Maybe my interpretation is wrong, but I saw that "The above invocations [without plus sign] activate the given profiles in place of the defaults. To activate a profile in addition to the defaults, prepend it with a +" ( What am I missing?


Ah you are completely right. Just tested it. The default profiles load in addition to the profiles specified in with-profile. Good to know 🙂


Now the + makes sense lol


Thank you for testing. I was kind in doubt after you said it overrides hehe Cool it is now clarified.


hello everyone, does any library exist that would make it easier to consume bytes from a websocket until a particular delimiter is reached, rather than just looking at individual frames?


i suppose it would be reasonably straightforward to build such a thing with core.async but i’d rather not duplicate the effort if possible


I have installed clojure into Ubuntu. I have : openjdk 14.0.1 2020-04-14 OpenJDK Runtime Environment (build 14.0.1+7-Ubuntu-1ubuntu1) OpenJDK 64-Bit Server VM (build 14.0.1+7-Ubuntu-1ubuntu1, mixed mode, sharing) I have a simple.core.clj file in /simple/src/simple and when I run clojure -m simple.core, I receive the following: clojure -m simple.core Execution error (FileNotFoundException) at clojure.main/main ( Could not locate simple/core__init.class, simple/core.clj or simple/core.cljc on classpath. Full report at: /tmp/clojure-15616630179164666709.edn I installed apt install clojure however I suspect I did not generate the .clojure folders and all the edn files. Well, I cant see them - e.g. the should be a deps.edn in the /simple folder


which "clojure" tool are you using?


usually they would expect simple.core to be in ./src/simple/core.clj or alternatively ./src/clj/simple/core.clj


I would suspect that sudo apt install clojure is something that very few production Clojure users ever touch or recommend.


yeah, debian's clojure doesn't use deps.edn, or even project.clj - it's a hacky standalone


the proper way to use clojure (as with any java lib) is with a project manager that manages your deps and classpath


it's not impossible to manage your own classpath, but there's little benefit


I would recommend sudo apt purge clojure , followed by using the instructions here: If you want to use Leiningen, that is another common way, but only necessary if you want to interact with a project that someone developed using Leiningen (indicated by presence of a project.clj file in the code base)

💯 1

OK I will follow the guide


Funny enough the linuxbrew did not work either. I am trying to learn how to move away from leiningen


I know it might seem strange, but I believe that the packaged versions in Debian/Ubuntu, and a few other package managers, are created by enthusiasts of those packaging systems, and also of Clojure, but what they install often has little to do with the instructions.


or worse, puts commands in your command path that have the same names as the installers on, which thus shadow/conflict with the recommended ones.


When you say the linuxbrew did not work either, do you mean the instructions on did not work for you on Linux?


Followed and doing the obvious works 😠


i've created a simple ring app and am wondering how do i pull a list of all routes


in the repl


ring doesn't actually say anything about routes, routing will come from some other library


ah i'm using compojure-api


the most common for a "simple ring app" being compojure


compojure routes are functions, which are not really queryable


all you can do is invoke the function on a request and either get a response when it matches, or nil when it doesn't


the documentation mentions a routing table but I can't find a call to it


ah, it does look like compojure-api adds some stuff on top of compojure for this

hiredman20:06:19 will work, as long as you only use the stuff from comojure-api, if you use vanilla compojure or wrap things in middelware it won't


the top of the compojure-api readme says "Psst! If you're starting a new project, why not try out"


Glad you mention reitit! My first project I used reitit but could not generate a war file! Still unable to. so for my second i did compojure and that worked out well. I like the work the metosin guys do so when i saw they created compojure-api i wanted to use that. Able to create a war file but my latest problem is


@U06GMV0B0 I'm curious: why are you trying to generate a WAR file?


I figured the war file would be the least intrusive for our devops team. We are predominantly dotnet shop and use azure service fabric. I'm not officially a developer just love to dabble so I like to make sure what i deliver is hassle free but my primary reason for it is i assume that tomcat manages memory and keeps apps alive especially after reboots. If embedding jetty gets me there without complication then I can definitely pitch that.


You could use embedded Jetty and package the app as an uberjar, so it could be run with java -jar my-clojure-app.jar if devops would go for that.


to resolve this issue i comment out just these 2 and everything works (there are 65 others) (GET "/tasks/" req      `:return [Task]`      `:query-params [subprojectid :- Int]`      `:summary "Returns a list of tasks for a given subproject"`      `(get-tasks db (:params req)))`      `(GET "/tasks/:taskid" req`      `:return Task`      `:path-params [taskid :- Int]`      `:summary "Returns task details"`      `(get-task db (:params req)))`


You can use triple backticks around a multi-line block of code. It's more readable than having each line formatted with single backticks.

👍 1

like this
of text


(GET "/tasks/" req
     :return [Task]
     :query-params [subprojectid :- Int]
     :summary "Returns a list of tasks for a given subproject"
     (get-tasks db (:params req)))
   (GET "/tasks/:taskid" req
     :return Task
     :path-params [taskid :- Int]
     :summary "Returns task details"
     (get-task db (:params req)))


cool thanks!


this is why i was wondering if there was a way to pull the "routing table" and see if i have any duplicates/conflicts


I cannot use macros as filter predicate?


note directly, as you can't properly use a macro as an argument to a function


of course you can use #(or ...) etc.

thanks 1
Brandon Olivier23:06:14

Does anybody else have trouble getting Firefox to read edn properly in the response devtools?