Fork me on GitHub
David Reno01:03:56

raspberry pi question: I’m trying to setup an IDE where I run emacs on my mac, edit the files locally (mapped to a local volume), and have cider connect to the repl on the pi over the local net. 1. is this kind of setup known to work? 2. should I install lein on the pi or is there a better approach?

David Reno01:03:21

I’ve installed azul jdk 11 on the pi and am able to locally edit files via emacs at this point.


if you can avoid using lein, it will be much less overhead - if you need CIDER you can add nrepl as a project dep and start up an nrepl server in a temporary -main - you'll need to override the host it listens on by default it's local only


otherwise you can make an uberjar with all your deps in it with lein, then start it up in an ssh session with rlwrap java -cp my-uber.jar clojure.main - you get a less featureful repl but use less system resources on the pi

David Reno01:03:18

I don’t think I need lein. Not really sure what the minimum is, do I need to install clojure on the pi? From googling this there were references to installing lein which I supposed installs clojure.

David Reno01:03:17

sorry if this is confusing, I’m very much a noob and have only ever run clojure locally on my mac.

David Reno01:03:31

The idea of what’s required remotely vs locally confuses me.


you don't need clojure, clojure is a java library and will be in your jar

David Reno01:03:54

I expect to use Cider to connect to a remote repl.


then you need to add nrepl as a dep to your project, and start an nrepl server


it's a small amount of code at least


and you'll need to change the :host of the server to

David Reno01:03:06

I’m confused about how the repl gets run on the pi. There must be some sort of clojure interpreter that needs to be installed on the pi, right?

David Reno01:03:18

or does all the clojure stuff occur on the local mac and then get sent over to the pi via the CIDER nrepl connection?

David Reno01:03:31

as java bytecode?


That’s the point of the nrepl server. It listens for commands n runs them in your java/clojure process on the pi and sends them back to emacs


No java on the emacs side


clojure itself is a bytecode compiler


it's a jar, java runs it if you ask it to

David Reno01:03:38

ok, so to make sure I understand, I only need java on the pi.


right - and you can have a git checkout or a jar or both to provide the code for java to execute

David Reno01:03:07

lol, I’m starting with a very limited understanding of the architecture! I guess I should read up on nrepl and cider before I ask more questions.


or just send everything over the repl


cider is great for features, but it adds a lot of complexity too once you do something sufficiently unusual - doing this without cider is much easier

David Reno01:03:26

ok, so the simplest thing is not to use cider? I think I’ve only ever used cider for local clojure. so I’ll edit the clojure files in emacs but not use cider? Any links to what I should read about that?


anything that works in a file (with very few exceptions) works when sent to a repl


you can literally dump every namespace into the repl (as long as you get the dependency order of namespaces right) and it will work

David Reno01:03:39

a part that confuses me (a lot of this confuses me) is that it seems that something should be listening on the pi for the connection from the mac.


right, it runs sshd by default so you can ssh in


and then you can start a repl inside ssh

David Reno01:03:13

how do I start a repl in the pi, I thought that was accomplished via lein or something similar. Not by java which is all I thought I needed on the pi.


inf-clojure is a socket-repl client for emacs you can use instead of cider

David Reno01:03:27

again, sorry for the confusion. this must be difficult. If I’m developing locally on my mac, editing source files there, and then evaluating on the pi, then surely something must run on the pi to pass instructions over to the JVM, no?

David Reno01:03:59

My best guess of the architecture: 1. edit the files on the mac via emacs (which has the effect of updating the files locally on the pi since they are network mapped) 2. use emacs minor mode to connect to an nrepl on the pi (I thought this meant cider but seemingly it does not) 3. evaluating sexp in emacs passes the sexp over to the nrepl running on the pi, somehow it’s interpreted to java bytecode there and run on the pi jvm.


I've always simply ssh'd into the pi and ran Clojure and emacs from there

David Reno01:03:07

that might be the simplest solution until I get a better understanding of the components involved here. I’ll need to watch some youtubes on nrepl I think!

David Reno01:03:18

Thanks everyone for bearing with my ubernoob questions.

ryan echternacht02:03:10

What’s the rationale between using ::keyword vs. :keyword? What conventions drive using one or the other?


type them at the repl

ryan echternacht02:03:14

The ::keyword form is the fully qualified namespace form?


:: means "auto-resolve" the keyword. ::foo/bar will use the namespace alias foo to resolve it, ::bar will use the current namespace.


(ns some.example
  (:require [fuselier :as foo]))

::foo/bar ;=> :fuselier/bar
::bar ;=> :some.example/bar


Why would you store keywords inside namespaces?


@ordoflammae The keywords aren't "stored" inside a namespace. :foo/bar is just a "qualified keyword". There doesn't have to be a foo namespace.


The :: syntax uses namespaces (and aliases) to resolve the qualified name, but that's the only connection.


Qualified keywords are used heavily with clojure.spec so reading the rationale and/or guide for that will probably help explain it and


But qualified keywords are useful outside of Spec too.


For example, next.jdbc produces result sets with qualified keywords identifying the columns, where the table name is used as the qualifier (if available). So a SQL query that joins a person table and an address table, for example, and both tables might have id as their primary key -- and you'd get a result set where the rows have :person/id and :address/id so you can tell them apart.

Bill Phillips05:03:20

So I started a little side project playing around with Clojure just because I kinda liked my impression of how Rich Hickey cut his jibsail, because I had enjoyed working with Scheme in college, and because I was more than a little burned out on Android Java development And now I’m iteratively building a web app on a live, running server through an REPL This is more fun programming than I’ve had in years.

David Pham07:03:14

Welcome to the community :) and have fun!

Cris B09:03:23

Similar story here. I've had no more lisp exposure than browsing through SICP, but that and a couple of Rich Hickey talks had seeded the idea of trying Clojure some day. After a bit of a break from dev work I started playing with Clojure last week, to see if I could still find programming fun rather then tedious. And so far I really have. It's refreshing. I'm just working my way through a book or two and some small scale exercises for now, but I have a few interesting projects in mind.

Bill Phillips05:03:41

I’m surrounded by footguns and I couldn’t be happier

😀 4

Beginner question. What does :> mean in ClojureScript?

Jakob Durstberger08:03:31

Isnt’ everything starting with : a keyword?


I saw it here: On second look, this seems Hiccup syntax. Still don't know what it does though.


It’s a Reagent syntax for using JS React components


it’s not built in Clojure, just Reagent idiom


DatePicker is React component written in JS and you need to tell Reagent it should be converted from React component to Reagent


That makes sense. Thank you!

metal 1

Hi folks, I have a question that feels easy, but has eluded me so far. I want to check if a given datetime is within the hours of 8am and 8pm in the relevant timezone, using Java 8 time (``). As far as I can tell, I can easily check whether it is within 8-8 in my local timezone using instances of time/local-time; however, I am stuck on how to compare within the target timezone


What have you tried and did not work? Asking because I am curious.


I have this:

(defn outside-valid-hours? [timezone timestamp]
  (let [timestamp-tz (time/with-zone (time/zoned-date-time timestamp) timezone)
        event-time   (time/local-time timestamp-tz timezone)
        valid-start  (time/local-time 8)
        valid-end    (time/local-time 20)]
    (< valid-start event-time valid-end)))
But whatever I try I can only seem to specify the start and end dates at 8-8 local time


Another avenue I went down was using (.with timestamp-tz (time/local-time 8))


To the same effect


Or this (time/zoned-date-time (time/local-date timestamp-tz) (time/local-time 8) timezone)


I'd reach for TDD in this case. TDD would force you to state your expectations, and let you solve them one-by-one. Test cases could also make it easier to help you, as others can then get something to test against. It doesn't have to be a lot:

(defn plus [x y])

(assert (= 2 (plus 1 1)))
(assert (= 10 (plus 5 5)))


Thanks @U3X7174KS - I have some simple tests, but still stuck on where to go beyond that


Feel free to share any simple tests 🙂


For e.g.

(is (outside-valid-hours? "Etc/GMT+3" "2020-01-01T03:03:51.302Z"))
(is (outside-valid-hours? "Etc/GMT+3" "2020-01-01T19:03:51.302Z"))
(is (not (outside-valid-hours? "Etc/GMT+3" "2020-01-01T13:03:51.302Z")))
(is (not (outside-valid-hours? "Etc/GMT+3" "2020-01-01T07:03:51.302Z")))

👍 1

Which one of them are passing?


1st and 3rd, because they are outside valid hours for both local time (GMT) and GMT+3


Is it possible to write a test that specifically tests a timezone conversion? In the situation that I'm unable to make a larger function work, I try to test smaller concepts. I often discover having misunderstood some term, abstraction or concept.


For clojure cli, what does the S prefix (i.e. sDeps)stand for? I couldn’t find info for some of these commands in docs.



👍 1

which are you not finding?


I think everything should be doc'ed in clj -h or man clj


the list of commands are documented, i mean that I couldn’t find any specific mention of what the prefix stood (which made it harder for me to remember)


thanks for the feedback, will consider making that clearer

🙌 3
Gabriel Saliev18:03:51

Hi pals, I'm currently reading Clojure for the Brave and True and found myself stuck trying to understand what lazy sequences are, I've looked at some articles on the internet but the concept still feels foggy to me, can somebody explain it in more human-like language, thanks in advance!


I wrote this once, trying to explain lazy sequences to someone. See if this helps.

👍 1

I can try. “Lazy” means basically “on demand evaluation”. For example, (range) returns a lazy sequence of all integers starting at zero and going up. If you tried to evaluate that, it’ll run forever, but you can execute (take 5 (range)) and you’ll get back (0 1 2 3 4), because range is lazy — it only evaluates as many values as it needs to satisfy what’s being asked for.

👍 2

That’s over-simplified, of course, but conceptually, that’s the idea. It’s a way to work with large or infinite sequences using only finite resources.

Phil Hunt18:03:21

Talking about Clojure for the Brave. Is the code there generally considered idiomatic?


I’d say in general, but if there’s any specific thing you have questions about feel free to ask.

Phil Hunt18:03:39

e,g, the way the hobbit bothering code uses destructuring on part & remaining, and recur to get the equivalent of a car/cdr recursion (kinda)


Do you happen to have a link? Haven’t looked at that chapter in a while.

Phil Hunt18:03:22

on: (let [[part & remaining] remaining-asym-parts] (recur remaining (into final-body-parts (set [part (matching-part part)]))))


Yeah, I’d say that’s reasonably idiomatic. There might be other approaches that would also be idiomatic, but that looks like something I might write.

Phil Hunt18:03:25

Cool, thanks


recur is how Clojure works around the lack of tail-call optimization in the JVM, so it’s commonly used in that sort of scenario.

Phil Hunt18:03:57

Yep, still getting to grips with that. But it suddenly hit me that the destructuring & rest thing is equivalent of car/cdr

Phil Hunt18:03:54

So I now feel the urge to try translating some tree and graph walking lisp into loop/recur

Phil Hunt18:03:17

Anyhow, thanks for the help

👍 1
Gabriel Saliev19:03:52

Thanks hindol, that's a pretty clean explanation!


@phil634 If you're looking for just car/cdr functionality in the recur, like processing elements one at a time (e.g. (set [part (matching-part part)])), I think it can be more idiomatic to use a reduce over your collection.

(reduce (fn [coll part]
          (into coll #{[part (matching-part part)]}) 
Also gives you a chance to factor out the function into something reusable. loop/recur is relatively speaking pretty manual, so if you can get away with cleaner/more readable methods with sequence functions I think it's generally more preferred

🙂 1

I remember reading that the gson object can be reused after serializing. But that made it sound ominously like it wasn’t thread safe


I'll do a quick test, why not


this sleeps between processing one value and processing the next, I think that makes the gson object get used concurrently

user=> (map deref (mapv (fn [v] (future (.toJson gson (cons "1" (lazy-seq (do (Thread/sleep 1000) [v])))))) (range 100)))
("[\"1\",0]" "[\"1\",1]" "[\"1\",2]" "[\"1\",3]" "[\"1\",4]" "[\"1\",5]" "[\"1\",6]" "[\"1\",7]" "[\"1\",8]" "[\"1\",9]" "[\"1\",10]" "[\"1\",11]" "[\"1\",12]" "[\"1\",13]" "[\"1\",14]" "[\"1\",15]" "[\"1\",16]" "[\"1\",17]" "[\"1\",18]" "[\"1\",19]" "[\"1\",20]" "[\"1\",21]" "[\"1\",22]" "[\"1\",23]" "[\"1\",24]" "[\"1\",25]" "[\"1\",26]" "[\"1\",27]" "[\"1\",28]" "[\"1\",29]" "[\"1\",30]" "[\"1\",31]" "[\"1\",32]" "[\"1\",33]" "[\"1\",34]" "[\"1\",35]" "[\"1\",36]" "[\"1\",37]" "[\"1\",38]" "[\"1\",39]" "[\"1\",40]" "[\"1\",41]" "[\"1\",42]" "[\"1\",43]" "[\"1\",44]" "[\"1\",45]" "[\"1\",46]" "[\"1\",47]" "[\"1\",48]" "[\"1\",49]" "[\"1\",50]" "[\"1\",51]" "[\"1\",52]" "[\"1\",53]" "[\"1\",54]" "[\"1\",55]" "[\"1\",56]" "[\"1\",57]" "[\"1\",58]" "[\"1\",59]" "[\"1\",60]" "[\"1\",61]" "[\"1\",62]" "[\"1\",63]" "[\"1\",64]" "[\"1\",65]" "[\"1\",66]" "[\"1\",67]" "[\"1\",68]" "[\"1\",69]" "[\"1\",70]" "[\"1\",71]" "[\"1\",72]" "[\"1\",73]" "[\"1\",74]" "[\"1\",75]" "[\"1\",76]" "[\"1\",77]" "[\"1\",78]" "[\"1\",79]" "[\"1\",80]" "[\"1\",81]" "[\"1\",82]" "[\"1\",83]" "[\"1\",84]" "[\"1\",85]" "[\"1\",86]" "[\"1\",87]" "[\"1\",88]" "[\"1\",89]" "[\"1\",90]" "[\"1\",91]" "[\"1\",92]" "[\"1\",93]" "[\"1\",94]" "[\"1\",95]" "[\"1\",96]" "[\"1\",97]" "[\"1\",98]" "[\"1\",99]")


you need to mapv creating the future or put a doall in there


oh right, thanks


c/data.json strips the : from keywords


there might be some concurrency without the forcing because of chunking anyway


no, it encodes keywords as strings


yeah, removing the :


oh in comparison to gson above, right


that's easy to adjust with a custom serializer of course, perhaps a clojure Gson serdes with good clojure defaults would be worth making