Fork me on GitHub
#beginners
<
2020-03-26
>
dcreno01: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?

dcreno01:03:21

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

noisesmith01:03:53

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

noisesmith01:03:57

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

dcreno01: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.

dcreno01:03:17

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

dcreno01:03:31

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

noisesmith01:03:43

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

dcreno01:03:54

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

noisesmith01:03:14

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

noisesmith01:03:52

it's a small amount of code at least

noisesmith01:03:02

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

dcreno01: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?

dcreno01: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?

dcreno01:03:31

as java bytecode?

dpsutton01:03:44

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

dpsutton01:03:49

No java on the emacs side

noisesmith01:03:55

clojure itself is a bytecode compiler

noisesmith01:03:04

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

dcreno01:03:38

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

noisesmith01:03:06

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

dcreno01: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.

noisesmith01:03:14

or just send everything over the repl

noisesmith01:03:55

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

dcreno01: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?

noisesmith01:03:28

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

noisesmith01:03:10

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

dcreno01: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.

noisesmith01:03:59

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

noisesmith01:03:07

and then you can start a repl inside ssh

dcreno01: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.

porkostomus01:03:58

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

dcreno01: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?

dcreno01: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.

porkostomus01:03:49

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

dcreno01: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!

dcreno01:03:18

Thanks everyone for bearing with my ubernoob questions.

ryan07202:03:10

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

lockdown-02:03:07

type them at the repl

ryan07202:03:14

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

seancorfield02:03:37

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

seancorfield02:03:27

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

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

ordoflammae03:03:00

Why would you store keywords inside namespaces?

seancorfield03:03:32

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

seancorfield03:03:21

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

seancorfield03:03:36

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

seancorfield03:03:51

But qualified keywords are useful outside of Spec too.

seancorfield03:03:13

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.

jings.bill05: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.

neo255107:03:14

Welcome to the community :) and have fun!

cb.lists09: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.

jings.bill05:03:41

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

hindol.adhya08:03:16

Beginner question. What does :> mean in ClojureScript?

jakob.durstberger08:03:31

Isnt’ everything starting with : a keyword?

hindol.adhya08:03:16

I saw it here: https://clojurians.slack.com/archives/C03S1L9DN/p1585198253017000 On second look, this seems Hiccup syntax. Still don't know what it does though.

andrzej.fricze09:03:12

It’s a Reagent syntax for using JS React components

andrzej.fricze09:03:34

it’s not built in Clojure, just Reagent idiom

andrzej.fricze09:03:20

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

hindol.adhya09:03:09

That makes sense. Thank you!

ben60612:03:06

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 (`clojure.java.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

hindol.adhya12:03:11

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

ben60612:03:56

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

ben60612:03:43

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

ben60612:03:49

To the same effect

ben60612:03:34

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

teodorlu12:03:42

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

ben60613:03:50

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

teodorlu13:03:18

Feel free to share any simple tests 🙂

ben60613:03:29

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

teodorlu13:03:38

Which one of them are passing?

ben60614:03:18

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

teodorlu14:03:11

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.

alidcastano15:03:05

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.

alexmiller16:03:40

which are you not finding?

alexmiller16:03:09

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

alidcastano16:03:26

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)

alexmiller16:03:45

thanks for the feedback, will consider making that clearer

4044159018: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!

hindol.adhya18:03:49

I wrote this once, trying to explain lazy sequences to someone. See if this helps. https://gist.github.com/Hindol/e6eba6bbc27289985ea7a5d242629d87

manutter5118:03:09

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.

manutter5118:03:51

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.

4044159018:03:18

Thanks! 🙂

phil63418:03:21

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

manutter5118:03:37

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

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

manutter5118:03:30

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

phil63418:03:22

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

manutter5118:03:02

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.

phil63418:03:25

Cool, thanks

manutter5118:03:28

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.

phil63418:03:57

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

phil63418:03:54

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

phil63418:03:17

Anyhow, thanks for the help

4044159019:03:52

Thanks hindol, that's a pretty clean explanation!

eagonmeng21:03:32

@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)]}) 
  remaining-asym-parts) 
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

dpsutton23:03:39

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

noisesmith23:03:06

I'll do a quick test, why not

noisesmith23:03:14

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

hiredman23:03:07

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

noisesmith23:03:19

oh right, thanks

lockdown-23:03:47

c/data.json strips the : from keywords

hiredman23:03:04

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

hiredman23:03:29

no, it encodes keywords as strings

lockdown-23:03:01

yeah, removing the :

noisesmith23:03:11

oh in comparison to gson above, right

noisesmith23:03:27

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