Fork me on GitHub
#beginners
<
2023-01-03
>
M J04:01:53

Hey, I have a sequence of said items, which is a r/atom actually. How do I remove the one with :id =1? ({:deleted_at nil, :value Market Research, :option_texts {:en Market Research}, :image_url nil, :strive_form_field_id 10212, :label Market Research, :id 27125, :order nil, :created_at 2022-12-21T11:16:44.80924} {:deleted_at nil, :value User Interviews, :option_texts {:en User Interviews}, :image_url nil, :strive_form_field_id 10212, :label User Interviews, :id 27126, :order nil, :created_at 2022-12-21T11:16:44.80924} {:id 1, :value Other, :order nil, :label Other})

bg04:01:46

(remove #(= 1 (:id %)) my-sequence-of-items)

mister_m05:01:02

is there a form of iterate that would allow me to not jam my state variables into a tuple as the single argument to the iteration fn?

mister_m05:01:09

for context, maybe I am doing an iterative fibonacci calculation and putting the n-2 and n-1 values into a tuple so I can get a lazy seq for free out of the iterate call

bg05:01:25

How about this? (take 10 (map first (iterate (fn [[a b]] [b (+ a b)]) [1 2]))) You can always destructure inside the iteration fn.

mister_m05:01:10

That's similar to what I landed on. It feels a little too much like I'm playing code golf though. For me at least it is a little difficult to see what that is doing at a glance. Maybe the destructuring obscures the b-for-a swap a little bit.

mister_m05:01:31

though to be fair, putting this in a (defn fib-iter ...) probably would be a big hint

mister_m05:01:01

would it be more "idiomatic" to just loop/recur and explicitly use lazy-seq

mister_m05:01:41

does clojure support grouping digits in numeric literals? IIRC Java allows the _ underscore to be used to group digits in a manner like 1_000_000 , wondering if that got into clj also

Alex Miller (Clojure team)05:01:54

no plans to add it at this time

👍 4
pppaul22:01:27

you can make a reader macro that does parse readable number formatting

pppaul22:01:04

#num "$1,000.00"

pppaul22:01:38

seems better than underscores

bschrag19:01:40

Any insight about using the Riddley walker? lein install of the https://github.com/ztellman/riddley clone fails, complaining about Java 6.

$ lein install
Compiling 1 source files to /media/sf_home/code/riddley/target/classes
warning: [options] bootstrap class path not set in conjunction with -source 6
error: Source option 6 is no longer supported. Use 7 or later.
error: Target option 6 is no longer supported. Use 7 or later.
Compilation of Java sources(lein javac) failed.
Is there another way to get it?

bschrag19:01:43

This, from Mastering Clojure Macros, also fails.

$ lein try org.clojars.trptcolin/riddley "0.1.7.1"
'try' is not a task. See 'lein help'.

Did you mean this?
         new
         run
         pom
         jar
         do
         test
         vcs

dpsutton19:01:08

❯ clj -Sdeps '{:deps {riddley/riddley {:mvn/version "0.2.0"}}}'
Clojure 1.11.1
user=> (doto 'riddley.walk require in-ns)
riddley.walk
riddley.walk=> (walk-exprs number? inc '(let [n 1] (+ n 1)))
(let* [n 2] (. clojure.lang.Numbers (add n 2)))
riddley.walk=>

dpsutton19:01:18

don’t lein install it. Just use the artifact

dpsutton19:01:19

but if you really want to have a local copy built, change this line to target java 1.8 instead of 1.6 i think

dpsutton19:01:43

I changed the line to use 1.8 and then

❯ lein uberjar
Compiling 1 source files to /private/tmp/riddley/target/classes
warning: [options] bootstrap class path not set in conjunction with -source 8
1 warning
Created /private/tmp/riddley/target/riddley-0.2.0.jar
Created /private/tmp/riddley/target/riddley-0.2.0-standalone.jar

riddley on  master [!] via ☕ v17.30 on ☁️  metabase-query took 6s
❯ clj -Sdeps '{:deps {riddley/riddley {:local/root "target/riddley-0.2.0-standalone.jar"}}}'
Clojure 1.11.1
user=> (doto 'riddley.walk require in-ns)
riddley.walk
riddley.walk=> (walk-exprs number? inc '(let [n 1] (+ n 1)))
(let* [n 2] (. clojure.lang.Numbers (add n 2)))
riddley.walk=>

dpsutton19:01:51

i’m able to build and use the resulting jar

bschrag19:01:18

@U11BV7MTK Trying to catch up with your idioms. Haven't used clj at the command line, just Cider (Emacs mode).

dpsutton19:01:58

that’s ok. i’m just using the basic tools without any editor tooling so you can see what is going on

dpsutton19:01:17

Are you wanting to develop with riddley or just use it in another project?

bschrag19:01:57

@U11BV7MTK Develop with it.

dpsutton19:01:09

All I’m demonstrating • in the first example, you don’t need to build it or install it locally. You can just use it the way you would use any other library: by using its coordinates in maven. Riddley is in https://central.sonatype.dev/artifact/pro.juxt.clojars-mirrors.ztellman/riddley/0.2.0 • if you want to change riddley, I’m demonstrating that i can run lein uberjar which builds the jars. And then use the artifact it created

dpsutton19:01:25

@U65G9MPCK do you mean make changes to riddley itself? Or just use it in another project?

bschrag19:01:39

Just use it.

dpsutton19:01:31

then just declare a dependency on it with [riddley "0.2.0"] for a leiningen project or riddley/riddley {:mvn/version "0.2.0"} for a clojure deps project

bschrag19:01:05

@U11BV7MTK I'd been trying something like that, ...

(defproject template-matcher "0.1"
  :description "FIXME: write description"
  :url ""
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url ""}
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [riddley "0.2.0"]
                 ]
  :repl-options {:init-ns template-matcher.core})
...but hit failure when I evaluate my core.clj buffer, including...
(ns template-matcher.core
  (:require [clojure.string :as str]
            [clojure.pprint :refer [cl-format]]
            [riddley.walk :refer [walk-exprs]]))
...with message...
Execution error (FileNotFoundException) at template-matcher.core/eval6256$loading (core.clj:1).
Could not locate riddley/walk__init.class, riddley/walk.clj or riddley/walk.cljc on classpath.

bschrag19:01:26

@U11BV7MTK The book says this should work, after the lein try thing.

(require '[riddley.walk :as walk])
I thought I'd just grab walk-exprs.

bschrag19:01:43

@U11BV7MTK Cleaning up the install as you suggested didn't help (same error).

dpsutton20:01:35

I just copied your project.clj from above:

❯ cat project.clj
(defproject template-matcher "0.1"
  :description "FIXME: write description"
  :url ""
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url ""}
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [riddley "0.2.0"]
                 ]
  )

❯ lein repl
nREPL server started on port 58069 on host 127.0.0.1 - 
REPL-y 0.5.1, nREPL 0.8.3
Clojure 1.11.1
OpenJDK 64-Bit Server VM 17.0.1+12-LTS
    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=> (doto 'riddley.walk require in-ns)
riddley.walk
riddley.walk=> (walk-exprs number? inc '(let [n 1] (+ n 1)))
(let* [n 2] (. clojure.lang.Numbers (add n 2)))
riddley.walk=>

bschrag20:01:53

@U11BV7MTK Ok, that works fine for me, too. But I'm still getting the error in my Cider REPL.

bschrag20:01:08

template-matcher.core> (doto 'riddley.walk require in-ns)
Execution error (FileNotFoundException) at template-matcher.core/eval6294 (form-init13654649044716525103.clj:324).
Could not locate riddley/walk__init.class, riddley/walk.clj or riddley/walk.cljc on classpath.

dpsutton20:01:49

Have you restarted your repl?

bschrag20:01:06

Many times.

bschrag20:01:07

@U11BV7MTK Time to ask about this in #C0617A8PQ?

dpsutton20:01:32

at the top of the repl you should see some information about “startup form”. Can you paste that here?

bschrag20:01:38

@U11BV7MTK

;; Connected to nREPL server - 
;; CIDER 1.5.0 (Strasbourg), nREPL 0.9.0
;; Clojure 1.11.1, Java 17.0.5
;;     Docs: (doc function-name)
;;           (find-doc part-of-name)
;;   Source: (source function-name)
;;  Javadoc: (javadoc java-object-or-class)
;;     Exit: <C-c C-q>
;;  Results: Stored in vars *1, *2, *3, an exception in *e;

dpsutton20:01:02

there should be the phrase startup form just below this?

bschrag20:01:45

@U11BV7MTK I don't see that. Here's the whole thing.

*** Closed on Tue Jan  3 11:53:45 2023 ***
;; Connected to nREPL server - 
;; CIDER 1.5.0 (Strasbourg), nREPL 0.9.0
;; Clojure 1.11.1, Java 17.0.5
;;     Docs: (doc function-name)
;;           (find-doc part-of-name)
;;   Source: (source function-name)
;;  Javadoc: (javadoc java-object-or-class)
;;     Exit: <C-c C-q>
;;  Results: Stored in vars *1, *2, *3, an exception in *e;
;; ======================================================================
;; If you're new to CIDER it is highly recommended to go through its
;; user manual first. Type <M-x cider-view-manual> to view it.
;; In case you're seeing any warnings you should consult the manual's
;; "Troubleshooting" section.
;;
;; Here are a few tips to get you started:
;;
;; * Press <C-h m> to see a list of the keybindings available (this
;;   will work in every Emacs buffer)
;; * Press <,> to quickly invoke some REPL command
;; * Press <C-c C-z> to switch between the REPL and a Clojure file
;; * Press <M-.> to jump to the source of something (e.g. a var, a
;;   Java method)
;; * Press <C-c C-d C-d> to view the documentation for something (e.g.
;;   a var, a Java method)
;; * Print CIDER's refcard and keep it close to your keyboard.
;;
;; CIDER is super customizable - try <M-x customize-group cider> to
;; get a feel for this. If you're thirsty for knowledge you should try
;; <M-x cider-drink-a-sip>.
;;
;; If you think you've encountered a bug (or have some suggestions for
;; improvements) use <M-x cider-report-bug> to report it.
;;
;; Above all else - don't panic! In case of an emergency - procure
;; some (hard) cider and enjoy it responsibly!
;;
;; You can remove this message with the <M-x cider-repl-clear-help-banner> command.
;; You can disable it from appearing on start by setting
;; 'cider-repl-display-help-banner' to nil.
;; ======================================================================
Execution error (FileNotFoundException) at template-matcher.core/eval6289$loading (core.clj:1).
Could not locate riddley/walk__init.class, riddley/walk.clj or riddley/walk.cljc on classpath.
template-matcher.core> (doto 'riddley.walk require in-ns)
Execution error (FileNotFoundException) at template-matcher.core/eval6294 (form-init13654649044716525103.clj:324).
Could not locate riddley/walk__init.class, riddley/walk.clj or riddley/walk.cljc on classpath.
template-matcher.core> 

dpsutton20:01:57

for instance, I started up CIDER from the project.clj file i copied from you earlier and see this

;; Connected to nREPL server - 
;; CIDER 1.2.0 (Nice), nREPL 0.9.0
;; Clojure 1.11.1, Java 17.0.1
;;     Docs: (doc function-name)
;;           (find-doc part-of-name)
;;   Source: (source function-name)
;;  Javadoc: (javadoc java-object-or-class)
;;     Exit: <C-c C-q>
;;  Results: Stored in vars *1, *2, *3, an exception in *e;
;;  Startup: /Users/dan/bin/lein update-in :dependencies conj \[nrepl/nrepl\ \"0.9.0\"\] -- update-in :plugins conj \[cider/cider-nrepl\ \"0.27.4\"\] -- repl :headless :host localhost
user> (doto 'riddley.walk require in-ns)
riddley.walk
riddley.walk> (walk-exprs number? inc '(let [n 1] (+ n 1)))
(let* [n 2] (. clojure.lang.Numbers (add n 2)))
riddley.walk> 

bschrag20:01:40

@U11BV7MTK I see I have a later version of CIDER (1.5.0), same version of nREPL. I also have a slightly newer version of Java (17.0.5).

dpsutton20:01:02

fair enough.

dpsutton20:01:16

can you remove the init-ns so that you don’t start up in the namespace of your project?

dpsutton20:01:49

That way you can get a repl up and running without blowing up

dpsutton20:01:57

then evaluate (System/getProperty "java.class.path") and paste it here

dpsutton20:01:17

My suspicion is that you built a bad jar earlier and then installed it so you aren’t getting a correct copy of riddley

bschrag20:01:44

Now I get the startup info:

;;  Startup: /usr/local/bin/lein update-in :dependencies conj \[nrepl/nrepl\ \"0.9.0\"\] -- update-in :plugins conj \[cider/cider-nrepl\ \"0.28.5\"\] -- repl :headless :host localhost

dpsutton20:01:12

what changed that you get startup info now?

bschrag20:01:21

When I removed :repl-options.

bschrag20:01:45

Acts better, now.

user> (doto 'riddley.walk require in-ns)
riddley.walk
riddley.walk> 

bschrag20:01:24

riddley.walk> (System/getProperty "java.class.path")
"/media/sf_home/code/Cogex/template-matcher/test:/media/sf_home/code/Cogex/template-matcher/src:/media/sf_home/code/Cogex/template-matcher/dev-resources:/media/sf_home/code/Cogex/template-matcher/resources:/media/sf_home/code/Cogex/template-matcher/target/classes:/home/bschrag/.m2/repository/cider/cider-nrepl/0.28.5/cider-nrepl-0.28.5.jar:/home/bschrag/.m2/repository/nrepl/nrepl/0.9.0/nrepl-0.9.0.jar:/home/bschrag/.m2/repository/org/clojure/clojure/1.11.1/clojure-1.11.1.jar:/home/bschrag/.m2/repository/org/clojure/spec.alpha/0.3.218/spec.alpha-0.3.218.jar:/home/bschrag/.m2/repository/org/clojure/core.specs.alpha/0.2.62/core.specs.alpha-0.2.62.jar:/home/bschrag/.m2/repository/riddley/riddley/0.2.0/riddley-0.2.0.jar:/home/bschrag/.m2/repository/org/nrepl/incomplete/0.1.0/incomplete-0.1.0.jar"

bschrag20:01:50

Sorry, got out of sync with your requests...

dpsutton20:01:08

what happens if you evaluate (require 'template-matcher.core)?

bschrag20:01:10

riddley.walk> (require 'template-matcher.core)
Syntax error compiling at (template_matcher/core.clj:327:5).
Unable to resolve symbol: walk-exprs in this context

bschrag20:01:37

I can see the defn for walk-exprs in walk.clj (in riddley). But I am not a namespace expert. Let me try the book's form.

bschrag20:01:56

@U11BV7MTK Better now.

riddley.walk> (require 'template-matcher.core)
nil

dpsutton20:01:11

what did you change?

bschrag21:01:10

[riddley.walk :as walk] --> [riddley.walk :refer [walk-exprs]] I had left walk-exprs unqualified, in source, when trying the former.

dpsutton21:01:37

glad you are sorted

bschrag21:01:58

Thanks, but I'm still a little mystified.

dpsutton21:01:34

you changed several things at once. We finally got it fixed but you had uses of walk-exprs in your code but didn’t introduce that

dpsutton21:01:54

now i would restore the repl-options or whatever and see if it works again

bschrag21:01:03

I had tried this many times with the :refer form---it was a temporary inconsistency. Restoring :repl-options...

bschrag21:01:35

@U11BV7MTK Working fine now with :repl-options. I'd thought recompiling riddley would overcome any former bad info. And maybe it did. I did realize just a few restarts ago that Cider's C-c M-r (restart REPL) doesn't clean things out as well as killing the nREPL buffer and redoing C-c M-j (new REPL). So, maybe both of those things. Anyway, now I seem good to go! Thanks so much!

mukundzare22:01:53

I have an itching question since a while now and I might sound stupid asking it but I would like to ask it anyway.. When someone says they hotfixed a patch directly in a running production app using the REPL, what are they implying exactly? My interpretations: 1. A REPL server was spawned the moment the app got started via lein run or directly though the uberjar for e.g.. Later, when a bug was discovered, the developers could connect to the REPL server remotely and redefine any functions on the fly to "hotfix" them. This way the REPL and the app shared the same state of the app. I am not really clear on how to go about starting the app with such a state-sharing REPL. 2. When the app/service was started via lein run or similar commands, there were no running REPL servers which spawned with the app. However, anyone can start a REPL server in the project folder via lein repl on the remote machine and jack into it from their local editor, find the root cause of the problem, fix it, commit the hotfix into the git repo, redeploy the app, and restart the app/service to pick up the changes via lein run.

👀 4
Alex Miller (Clojure team)22:01:39

seems like a good question to me! I think usually people mean #1 - changing the running function to a "fixed" version via a connected repl (and making the same change in the source for future deploys)

Alex Miller (Clojure team)22:01:59

so the question then is how to start the repl server in your app. Clojure ships with the ability to act as a socket server by just adding some system properties (see https://clojure.org/reference/repl_and_main#_launching_a_socket_server) and maybe some people do that. Alternately, you can programmatically do the same thing so you might make some way to start a server on demand via any means of interaction you might put into your server

Alex Miller (Clojure team)22:01:13

and as far as the "state sharing" - if you connect to the same Clojure runtime, you get that automatically. if you connect via a remote repl to the JVM running Clojure, there is only one namespace, and only one var at a specific name so doing an alter-var-root or equivalent will affect what the app is running (unless you have used direct linking and applied an uber jar, in which case, more drastic efforts may be required)

Alex Miller (Clojure team)22:01:32

is any of this a good idea operationally? well....

😃 8
mukundzare22:01:55

Brilliant. I work in a tool for my day job which is actually based on Allegro CL and we have a socket REPL there too. It uses a bit of a different architecture and hence my assumption was also #1 because that's exactly what I do for our production issues 😁 . It was not clear to me while reading the docs about how the REPL spawns with the app. I guess I skipped the section on Socket REPLs thinking it was just an alternative for the nREPL. @U064X3EF3 your answer has helped me a lot to relieve me from what was bothering me since a few days. Great talk at reClojure BTW. I just watched it 2 hours back 🙂

2
practicalli-johnny22:01:03

I agree its scenario #1 Running Clojure always includes a REPL process, however it may not be configured to accept an external connection, e.g via nREPL or SocketServer. If a hot-fix approach is desirable, then the production app would be run with either an nREPL server or SocketServer and then code can be evaluated either at the REPL prompt or via a connected editor. Obviously changes like this should not be taken lightly and security should be in place so that changes cant be made without authorisation and should be done over a secure connections (probably connecting securely to the server first, then connecting to the REPL) If there are multiple instances of the service, then this hot-fix could cause some very interesting 'challenges'. So having an efficient CI / CD process is more consistent approach

seancorfield23:01:24

@U023570MX0R I just recently blogged about how we do this with nREPL servers on QA and production: https://corfield.org/blog/2022/12/18/calva-joyride-portal/

mukundzare23:01:01

@U05254DQM: what is the reason behind having a REPL process included if one never would want to use it? Should it not be more explicit to start the process because launching a shadow unaccessible REPL process sounds like a waste of processing power.

seancorfield23:01:15

We've run Socket Servers in production for ages -- by specifying a JVM option to start it up when Clojure starts (per what Alex said) but recently decided we wanted the full "development" experience by adding nREPL (and Portal -- so I can visualize production data in a web browser connected to it).

mukundzare23:01:51

The whole confusion began because of your excellent REPL driven development video I watched @U04V70XH6.

2
mukundzare23:01:43

You did mentioned about the running REPL servers there and I was left scratching my head.. But I will go through the blog above

seancorfield23:01:58

Haha... sorry to confuse 🙂 Hopefully that blog post helps.

❤️ 2
seancorfield23:01:51

We don't patch any production processes that way (we build uberjars with direct linking for deployment) but it's very helpful to debug production issues, especially being able to visualize data via Portal.

seancorfield23:01:19

(we can easily & automatically deploy updated JARs to production so hot-fixing things isn't worth the risk/complexity at this point)

andy.fingerhut01:01:50

The REPL server is not a separate process - it is I believe a listening socket that can accept a connection, and if such a socket is created, then there is a thread running a REPL reading input from that socket, and sending output back to it.

💯 4
andy.fingerhut01:01:39

That thread runs in the same process that is the JVM process you started to start your application.

jaihindhreddy06:01:21

> I might sound stupid asking it but I would like to ask it anyway.. "The man who asks a question is a fool for a minute, the man who does not ask is a fool for life." - Confucius.

❤️ 2
jumar07:01:18

I’ve been using socket repl for years, occasionally, to debug tricky production issues. It’s been an invaluable tool for me. I very rarely change stuff this way because it’s a bit dangerous and needs to be done on multiple machines

adi09:01:12

Paging @U051S5XR3 for production REPL stories :)

💯 2
mukundzare10:01:42

@U06BE1L6T I have been also doing it but in a different tool called Planisware which has an inbuilt REPL which I am used to. The tool is itself programmed in Allegro Common Lisp but provides a REPL to write code in an internal language called Planisware Script which is closely following ECMAScript which gets internally interpreted/compiled down to CL. It is one of the reasons why I feel at home in Clojure and had very little trouble with understand REPL-driven development 😀

2
adi14:01:06

From the video, if you're @U051S5XR3, then this is what it looks like to be always-connected to eight production REPLs 🤓

dpsutton16:01:00

I have used socket repls exclusively for the last two years or so. One thing I absolutely love about it is that there is essentially no difference between my “dev” experience with our repo and a repl into a production jar. From my point of view, there’s no difference between clj -A:dev:socket and java "$(socket-repl 6000)" -jar metabase.jar . They are functionally indistinguishable. I have a little shell function called socket-repl that expands out to the full incantation to set up a socket repl with the jvm option.

👍 2