Fork me on GitHub
#beginners
<
2020-12-28
>
Fra00:12:22

Hi, I’m sure I am using for in the wrong way, but out of curiosity, is there a function to turn a nested list such as (([[#{...} #{}] [#{...} #{...}]])) into something like [[#{...} #{}] [#{...} #{...}]] ? the function flatten gives a different result. Thanks

euccastro00:12:58

that would work for your particular example. I don't know whether you have a more general pattern in mind. if so, please give an example where ffirst wouldn't work

Fra00:12:23

ffirst seems to work, thanks @euccastro πŸ‘

πŸ‘ 3
Michael W03:12:07

If I want to curry a function with 2 arguments, supplying the second argument, partial won't work. Is there anything that allows partial application of out-of-order arguments?

seancorfield04:12:25

@michael819 That's what anonymous functions are for. And Rich Hickey says they are more idiomatic than partial anyway. #(f % 42) for a literal of f with the second argument provided.

popeye06:12:36

The difference between and @ is , is used to unquote variables and @ used for collections, Is my understanding correct?

Shantanu Kumar06:12:44

You could also use ~ for whole expressions, as long as it’s just one expression.

popeye06:12:36

ok, But my above understanding is correct?

popeye07:12:58

Thanks, can you give example for expression? is that ~(+ x y)

euccastro07:12:20

inserts a single thing (possibly a collection too!) and @ splices a collection

euccastro07:12:21

see

user=> (def a [:a :b :c])
#'user/a
user=> `(1 2 3 ~a 4 5)
(1 2 3 [:a :b :c] 4 5)
user=> `(1 2 3 ~@a 4 5)
(1 2 3 :a :b :c 4 5)

9
Shantanu Kumar07:12:23

@popeyepwr Yes, ~(+ x y) is a correct example (assuming x and y resolve).

euccastro07:12:17

here a is a variable and it evaluates to a collection

popeye07:12:48

@U066J7E2U both (+ x y) and (+'x 'y) gave error after assigning x and 7 as 5 and 7

Shantanu Kumar07:12:29

@popeyepwr When you use ~(+ x y) in a macro, x and y should be resolvable at compile time. To make it resolve at runtime, you need to use (+ ~x ~y).

euccastro07:12:30

can you paste the whole expression? ~ is supposed to be used within a quasiquoted (i.e., preceded by a backtick) expression

popeye07:12:53

@euccastro @U066J7E2U, That helped, Thanks πŸ™‚

euccastro07:12:14

user=> (def x 5)
#'user/x
user=> (def y 7)
#'user/y
user=> `(:a :b :c ~(+ x y) :d)
(:a :b :c 12 :d)
user=> `(:a :b :c ~(+ 'x 'y) :d)
Execution error (ClassCastException) at user/eval1 (REPL:1).
class clojure.lang.Symbol cannot be cast to class java.lang.Number (clojure.lang.Symbol is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')

euccastro07:12:34

the second case is an error because you are trying to add two symbols

euccastro07:12:57

` suppresses evaluation, ~ reenables it

euccastro07:12:55

you'd get the same error if you just tried to evaluate (+ 'x 'y), without using backquote or ~ at all

πŸ‘ 3
noisesmith16:12:47

@U066J7E2U the only difference between ~(+ x y) and (+ ~x ~y) is when the + call happens (during list building, or later when the compiled object is run) - the same variables are added in both cases (but they might have different values later)

Christian11:12:03

Hey there, I have some endless seqs of natural numbers, and I want to find the lowest number that is in all seqs. I'm not sure how to do it in a smart way. I could iterate through all natural numbers and check if it checks true for all the generation rules, but the final number is huge, so this will take a really long time. I'm sure there is a number-theory trick as well, but my residue system knowledge is not that good.

(def x0 (iterate #(+ 7 %) 7))
(def x1 (iterate #(+ 13 %) (+ 13 1)))
(def x2 (iterate #(+ 59 %) (+ 59 4)))
(def x3 (iterate #(+ 31 %) (+ 31 6)))
(def x4 (iterate #(+ 19 %) (+ 19 7)))

furiel11:01:29

You could try the Chinese remainder theorem: https://en.wikipedia.org/wiki/Chinese_remainder_theorem If you scroll down to Existence (direct construction) , you can use that formula. You need to find or implement an extended eucledian algorithm to calculate M_i and m_i though. Other than that, the formula can be calculated within reasonable effort.

euccastro11:12:22

(and yes, those numbers are coprimes)

euccastro11:12:48

there's #adventofcode too πŸ˜‰

Christian11:12:12

Oh, I haven't seen this channel, thanks πŸ™‚

Christian11:12:10

I was hoping to solve it myself. I feel like cheating, now that I have a hint.

Christian11:12:22

Hint might be more of a solution...

euccastro11:12:13

there's still some work to do

euccastro11:12:32

but I think that particular puzzle requires knowing that particular bit of number theory. if not, I don't think you can solve it in any other way than rederiving the theorem yourself, which is a bigger ask than any of the other AoC puzzles

☝️ 3
Christian11:12:44

I'd love to finish all days and discuss the solutions with others, is there some division of aoc divided by days?

Christian11:12:32

I even watched some youtube videos on modulo-ring-theory but they did not mention the chinese theorem

euccastro11:12:36

there are per-day threads in that channel. unfortunately some are old enough that you can't see them unless you have a paid Slack account

euccastro11:12:28

I remember having seen it when studying discrete math, but when I got to solve that puzzle I had already seen the spoiler, so I'll never know whether I would have recalled it on my own..

Christian11:12:12

So far I only had problems with the second adapter in plane part. I do these to learn clojure and it feels bad, when my math knowledge is blocking the path and not the understanding of clojure.

Christian11:12:23

I'm excited to finish it πŸ™‚

Christian11:12:32

Thanks for the channel and the method hint

euccastro11:12:22

I think that particular puzzle is the only one where math is a real blocker. in others math insights may get you a faster answer, but problems are still workable if you miss them

V12:12:11

I am trying to solve this problem on codewars: "Complete the functionΒ `scramble(str1, str2)`Β that returnsΒ `true`Β if a portion ofΒ `str1`Β characters can be rearranged to matchΒ `str2`, otherwise returnsΒ `false` " - But I am unsure how to approach it. I first did the naive thing and made both strings into sets. However that was not a possible solution, since if a specific character is repeated twice in str2 and only once in str1, then it should return false. Such that: (scramble "javscripts", "javascript") => false . Then I tried a solution of counting the characters and keeping the count in a dictionary. Then by using merge-with and the "-" function I could maintain if a character appeared more often in the str2 string. I tend to overcomplicate these things a lot and was wondering if there are smarter ways to do this? - This is my code so far

(defn character-count [st]
  (reduce #(assoc %1 %2 (inc (%1 %2 0)))
          {}
          (re-seq #"\D" st))
  )

(defn scramble [s1 s2]
  (let [s1-count (character-count s1)
        s2-count (character-count s2)
        total (merge-with - s1-count s2-count)
        ]
    (not (> (count
              (filter false?
                           (for [[k x] total
                                 :when (or (< x 0) (not (contains? s1-count k)))
                                 ]
                             false)))
            0))))
Link to task: https://www.codewars.com/kata/55c04b4cc56a697bb0000048/train/clojure

Max Deineko13:12:55

There's no need to walk the entire collection in (not (> (count (filter pred smth)) 0)) -- it's better to use some short-circuiting function like some, not-any? etc. Also, in this particular case, there's no need to check all values in total afaics -- you only need to check the keys of the second map to both be present in the first and have compatible value.

πŸ™Œ 3
βœ… 3
V12:12:00

I realized that I could have used frequencies instead of the character count function

2FO12:12:47

Hi, I installed CLJ CLI tools on Ubuntu via the following http://clojure.org scripts. How can I uninstall it and remove its dependencies from my system? "Use the linux-install script to download and run the install, which will create the executables /usr/local/bin/clj, /usr/local/bin/clojure, and the directory /usr/local/lib/clojure:" curl -O chmod +x linux-install-1.10.1.727.sh sudo ./linux-install-1.10.1.727.sh

noisesmith16:12:30

it doesn't install any dependencies, but it does install clj and clojure to /usr/local. it also creates some dot directories for cache and settings at runtime of clj and clojure, including ~/.clojure and ~/.m2 which is shared by other applications using the maving package system

πŸ™ 3
noisesmith16:12:01

I wonder if there's an authoritative list though

πŸ‘ 3
2FO19:12:06

So I guess its just a case of deleting the dot directories that I can find?

noisesmith19:12:18

sure - maybe someone else has the official doc on this (or, also possible, nobody made that doc)

noisesmith19:12:42

this is the repo for the installer if that helps - I don't see an uninstall or a document describing what it installs https://github.com/clojure/brew-install

2FO19:12:47

thanks, I'll check it out, just had a reread of the docs on http://clojure.org; no sign of removal instructions there either

noisesmith19:12:16

an empirical (but maybe silly) approach would be to make a minimal docker image with java, and run two instances. run the clojure installer in one of them, and find all the differences in the file system

noisesmith19:12:22

that's probably too much work though

2FO19:12:18

I'd consider it if I had any docker experience.. in the end I deleted ~/.m2 ~/.clojure and any dot directories I could find, then ran timeshift to restore my system to its pre- CLI tools state,

noisesmith19:12:56

seems like a decent feature request for that repo TBH

πŸ‘ 3
roelof13:12:28

what do the closures do here :

Instead of using a lazy list, imagine two threads are removing tasks from a pile of work. Our work pile will be the list of all integers from 0 to 10000:
user=> (def work (ref (apply list (range 1e5))))
user=> (take 10 @work)
(0 1 2 3 4 5 6 7 8 9)

And the sum will be a ref as well:
user=> (def sum (ref 0))

Write a function which, in a dosync transaction, removes the first number in work and adds it to sum.
Then, in two futures, call that function over and over again until there's no work left. Verify that @sum is 4999950000. Experiment with different combinations of alter and commute–if both are correct, is one faster? Does using deref instead of ensure change the result?
I find this a confusing challenge of the "clojure from the ground up" book

euccastro14:12:24

what closures?

roelof15:12:27

I have to make a function and then the text is saying need in two futures to do something but I do not see what the closures should do and wy I need 2 of them

euccastro15:12:21

I think you need two futures to test the thread safety of the ref/dosync mechanism

euccastro15:12:33

i.e., that they do the right thing despite contention

euccastro15:12:18

the futures just call your function (closure) over and over until (empty? @work)

roelof15:12:23

oke, so both futures do the same , call the function till work is empty

roelof19:12:26

oke, as you can see in later discussion I hit a lot of walls with this one

roelof19:12:51

this gives still zero

(defn add-dosync []
  (dosync
  (alter sum4 + (first @work))
  (alter work rest)))


(defn future1 [] 
  (future @work add-dosync))

(defn future2[] 
  (future @work add-dosync))

(deref sum4)

euccastro07:12:08

the futures are not checking for an empty @work and they are not calling add-dosync, just referencing it. see while for a way to run something until a condition becomes false. to call add-dosync just wrap your references in parentheses: (add-dosync)

jjttjj15:12:41

When trying to use https://github.com/thi-ng/geom, when I try to require .geom.viz.core In my large project I keep getting the error namespace '.color.core' not found. It works fine in an otherwise empty skeleton project. Any hints as to what might cause this?

jjttjj16:12:46

Figured it out. It was a conflicting prefer-method in my project and http://thi.ng.color.core

roelof15:12:38

Can I the best develop in WSL or can I also do everyhing with Windows ?

jjttjj15:12:35

I do everything in regular windows (https://github.com/littleli/scoop-clojure makes it easy for me). I've heard good things about WSL2 but haven't tried or needed it personally to do everything in clojure

jjttjj15:12:01

there's a #clj-on-windows channel that is helpful too

practicalli-johnny16:12:50

@roelof WSL is very good if you are used to the Unix command line. Also VS Code has some integration with WSL, making it easier to use with the Calva extension for VS Code.

FHE17:12:30

Hi. Does anyone here know how to get CIDER going in Doom Emacs?

FHE17:12:32

(I have also asked this question in the Doom Emacs Discord channel.)

FHE17:12:54

I don't know which is higher: The fraction of Clojure/ClojureScript programmers who use Doom Emacs, or the fraction of Doom Emacs users who program in Clojure/ClojureScript.

FHE17:12:54

Heart of the question is whether uncommenting in the "clojure" line (which sits commented out along with lines for lots of other languages etc by default) in the init.el file in the .doom.d folder (and running 'doom sync' of course) is enough. The loading stuff that triggered in Emacs seems to have included some mention of CIDER, but maybe it's a separate install??

FHE17:12:23

By the way, that init.el file in the .doom.d folder refers to Doom modules, and I'm not clear on what those are. in vanilla init.el file: (defvar my packages '( [...] clojure-mode )) in doom init.el: (doom! :input [...] clojure ) ...but the vanilla config also contains a separate line just underneath: cider

FHE17:12:05

I don't know how to use CIDER yet, but I do see that my doom emacs seems to recognize the 'cider-jack-in' M-x command (or whatever it is), so that in itself seems to prove the doom input clojure thing includes clojure-mode AND cider. Still, it would be nice to know for sure what a Doom Emacs module does.

andy.fingerhut17:12:51

Someone in this channel might know, but there is also a #cider channel on this Slack community that might be a more knowledgeable environment for your questions.

roelof17:12:35

Can anyone what is a good way to learn to make website's with clojure ? So any tutorials which I can follow or free books to read

roelof20:12:36

thanks, I will look at that one

benny17:12:33

@factorhengineering what channel in the Doom Emacs Discord? I think it's more appropriate there and I will try to help out

benny17:12:06

@roelof Fulcro has lots of videos and a very in-depth book. But it's also a full buy-in framework which I consider very well thought out. This is what I think of when I read free and tutorials. A more simpler option would be to check the beta book of "Web Development with Clojure, Third Edition" but that's not free

noisesmith17:12:02

there's also luminus, which is extensively documented (it's what "Web Development with Clojure" uses)

popeye18:12:12

how dynamic variable is used in the immutable language ? data-readers

noisesmith18:12:14

the language isn't immutable, vars are mutable containers

noisesmith18:12:25

in particular, dynamic vars (which have names that are *earmuffed* by convention), can be set in a way that's visible to functions you can call, without changing what parent functions see, this is called dynamic binding

noisesmith18:12:26

we encourage immutable code (and our standard data collections are immutable) - we use dynamic binding because it's safer than regular mutation

popeye18:12:03

@noisesmith Any example to understand it more?

noisesmith18:12:18

(cmd)user=> (def ^:dynamic *verbose* false)
#'user/*verbose*
(ins)user=> (defn f [x] (if *verbose* (println "found" x)) x)
#'user/f
(cmd)user=> (f 2)
2
(ins)user=> (binding [*verbose* true] (f 2))
found 2
2

noisesmith18:12:08

so what those vars are often used for is behaviors - where you want a changed behavior inside some block of code without having a config argument to keep track of

noisesmith18:12:45

with *data-readers* - the use case is that inside one block of code you are loading a config that uses specific readers

noisesmith18:12:18

you don't want to change the behavior of all clojure's code that reads data, just the usage inside a specific context

noisesmith18:12:51

so you use binding to add / modify the readers, and you know the behavior is only being configured inside that binding block and the things it calls

noisesmith18:12:02

binding is based on the call stack, and if you use standard clojure functions, it even works when a new thread is created

noisesmith18:12:54

(cmd)user=> (future (Thread/sleep 1000) (binding [*verbose* true] (f 2)))
#object[clojure.core$future_call$reify__8454 0x3703bf3c {:status :pending, :val nil}]
user=> found 2

(ins)user=> @*1
2

noisesmith18:12:49

so for example the case where you had two threads that are each reading a config file, without a dynamic var either you need a complex coordination to manage the global configuration of the readers, or you need to add an argument to the read function that specifies the readers - someone decided that having a dynamic var was simpler

noisesmith18:12:27

in practice, dynamic vars are used quite rarely in my experience

popeye18:12:29

Yeah I understood @noisesmith, Thanks

popeye18:12:36

what is @*1 ?

noisesmith18:12:13

*1 is always the last value the repl returned (there's also *2 and *3 for the values before that

noisesmith18:12:13

(ins)user=> "first"
"first"
(ins)user=> "second"
"second"
(ins)user=> "third"
"third"
(ins)user=> [*1 *2 *3]
["third" "second" "first"]

noisesmith18:12:26

@ is a reader macro that expands to a call to deref

noisesmith18:12:40

user=> `@f
(clojure.core/deref user/f)

andy.fingerhut18:12:41

It won't answer all of your questions, but the Clojure cheatsheet can be a handy way to look up some things like that: https://jafingerhut.github.io/. https://clojure.org/api/cheatsheet

noisesmith18:12:33

@popeyepwr your repl probably prints out an explanation of *1 etc. and other values like *e at startup - most people don't read things like that though

noisesmith18:12:21

(we are trained from many examples that big blocks of printout at the start of things are usually irrelevant, usually some kind of advertisement or legal boilerplate)

πŸ™Œ 3
chrisulloa18:12:30

How would you describe the benefit of this

(into [] (comp (map fn-1) (map fn-2) ...) coll)
over this
(->> coll (map (comp fn-1 fn-2 ...)) (into []))
? I was thinking that the second example doesn’t leave room in the comp for any filter/removes. Another thing I was thinking was that composing functions (comp fn-1 fn-2 fn-3) leads to (fn-3 (fn-2 (fn-1 val))) which builds a larger intermediate stack than what would occur in the first block for each value being processed.

noisesmith18:12:22

the benefit of the transducing version is that the ->> version creates lazy seqs that aren't needed

πŸ‘ 3
noisesmith18:12:58

the comp version still builds up a stack, it just doesn't build the lazy-seqs between the stack levels

noisesmith18:12:35

it actually builds and unbuilds the stack once per item, instead of once per function, but that's good because it's much cheaper than lazy-seq building

πŸ‘ 3
popeye18:12:51

Thanks @noisesmith , For your detailed explanation, It helped me

🍻 3
noisesmith18:12:06

(many people are used to "smart" compilers that automatically do things like removing unneeded intermediates, clojure intentionally avoids that kind of cleverness)

roelof18:12:07

why do I get here a illigalState error message :

; Write a function which, in a dosync transaction, removes the first number in 
; work and adds it to sum. Then, in two futures, call that function over and 
; over again until there's no work left. Verify that @sum is 4999950000. 
; Experiment with different combinations of alter and commute–if both are 
; correct, is one faster? Does using deref instead of ensure change the result?

(defn add-dosync[]
  (dosync
     (alter @sum4 + (first work) )))


(defn future1 [] 
  (future(seq @work) add-dosync))

(defn future2[] 
  (future(seq @work) add-dosync))

noisesmith18:12:30

@roelof what is the full error message?

noisesmith18:12:06

@roelof also what is sum4 - for add-dosync to make sense it needs to be a ref to another container you can deref, which seems very weird

roelof18:12:49

; Execution error (IllegalStateException) at ground-up.chapter6/eval14308 (form-init6272265265852733152.clj:88).
; No transaction running

noisesmith18:12:49

also (future (seq x) f) calls seq (which does very little) then returns f without calling it

roelof18:12:34

; Instead of using a lazy list, imagine two threads are removing tasks from a pile
; of work. Our work pile will be the list of all integers from 0 to 10000:

(def work (ref (apply list (range 1e5))))

; And the sum will be a ref as well:
(def sum4 (ref 0))

; Write a function which, in a dosync transaction, removes the first number in 
; work and adds it to sum. Then, in two futures, call that function over and 
; over again until there's no work left. Verify that @sum is 4999950000. 
; Experiment with different combinations of alter and commute–if both are 
; correct, is one faster? Does using deref instead of ensure change the result?

(defn add-dosync[]
  (dosync
     (alter @sum4 + (first work) )))


(defn future1 [] 
  (future(seq @work) add-dosync))

(defn future2[] 
  (future(seq @work) add-dosync))

(ensure sum4)

noisesmith18:12:55

you can't alter the number 0

noisesmith18:12:12

so first off, (alter @sum4 ...) doesn't make sense

roelof18:12:12

o, I was told by calva to check that work is not empty

noisesmith18:12:41

(future nil f) creates a delayed result that returns f

noisesmith18:12:19

(future something-not-nil f) creates a delayed result that also returns f

roelof18:12:35

oke, So I have to change to (future @work add-dosync)

noisesmith18:12:02

that derefs work, does noting with it, then returns a function

noisesmith18:12:18

this isn't how future works at all

noisesmith18:12:54

also your illegal state is on the last line (ensure sum4)

noisesmith19:12:13

ensure is for making a transaction retry if its arg changes before the transaction returns

noisesmith19:12:30

it doesn't do anything valid outside a transaction

roelof19:12:24

yep, with deref I get a answer of 0

noisesmith19:12:36

right, because you never call add-dosync

noisesmith19:12:51

you just create threads that return it (and never reference the threads again)

noisesmith19:12:22

in fact, you never even reference the function definitions that would have created those threads (another problem with this code)

roelof19:12:40

hmm back to the book (clojure from the ground up) to find out what I do wrong

noisesmith19:12:47

that's a good idea

noisesmith19:12:19

a simplified version: (future (f x)) calls f on x in a new thread - (future f) doesn't do anything interesting

noisesmith19:12:15

you probably got it mixed up with the way alter works (where you pass it some place, and a function that alters what is in that place)

noisesmith19:12:59

but you also got alter wrong, by giving it the value in the place, instead of the place itself

roelof19:12:05

thanks, this is a very difficult chapter

roelof19:12:44

if I understand you , I have to do something as add-dosyn <number> but according to the challenge I have to do something in the function to add a number to the sum

roelof19:12:07

and not in the future

noisesmith19:12:41

starting from the part of the code that seems closest to something that could work:

(dosync
     (alter @sum4 + (first work) ))
it becomes something almost reasonable if we change it to
(dosync
     (alter sum4 + (first @work)))
but there's still the problem that you also have to remove that first item from work so that no other call tries to use that same item

noisesmith19:12:19

so that becomes, perhaps

(dosync
  (alter sum4 + (first @work))
  (alter work rest))

noisesmith19:12:51

which adds the item to the sum, and removes it from work, and that's all in one transaction so you know that no other call to the same function used that item

noisesmith19:12:25

but perhaps you (or the exercise author) had something else in mind - there's enough wrong in the code that I could be on the wrong track about how to fix it

roelof19:12:40

now I have to find out how to use that in the future

popeye19:12:50

In the below code I assumed fun is evaluated if the input is positive and argument is between 16 and 225

popeye19:12:51

(defn constrained-sqr [x] {:pre [(pos? x)] :post [(> % 16), (< % 225)]} (* x x))

popeye19:12:01

Is my understanding right?

noisesmith19:12:23

it will throw an AssertionError if the constraints are not met

noisesmith19:12:35

it's evaluated up until the point one of the constraints fails

popeye19:12:07

it returned value for (constrained-sqr 7) but fails for (constrained-sqr 17)

noisesmith19:12:34

because 289 is greater than 255

popeye19:12:06

the result should be less? i though it is arguent

noisesmith19:12:22

:post is a test on the result

noisesmith19:12:31

(or rather, a vector of tests on the result which must all pass)

popeye19:12:18

if i pass 7, it should check for pos? right ... (pos? x)

noisesmith19:12:43

then it tests if the return value is between 16 and 255, exclusive

noisesmith19:12:19

% in the :post clause is a placehoder for what the function wants to return

popeye19:12:22

(pos? x) should fail for input 7 right.. since it is not +ve value

noisesmith19:12:46

user=> (pos? 7)
true

popeye19:12:19

My bad sorry!!!! confused with even, My bad!!!!

benny20:12:49

is there a LIFO stack based list-like type? I'm trying to keep the last 100 x's only

seancorfield20:12:31

benny a LIFO stack can use a vector with conj to add, peek to look at the "head" and pop to remove the "head" (it actually uses the backend of the vector, not the front end). But "keep the last 100 x's" only sounds more like a FIFO queue to me (where you would pop off the oldest item once you hit the queue limit). There's a clojure.lang.PersistentQueue for that.

benny20:12:03

@seancorfield oh yes, thanks! FIFO is right πŸ˜‰

emilaasa20:12:32

Are you queueing work or just keeping a collection of things?

benny20:12:11

@emilaasa just keeping a running tally of events and when a certain trigger happens I want to dump the last 100 events

emilaasa20:12:48

Sounds like a good use for PersistentQueue! Just wanted to make a quick shoutout to java.util.concurrent which also has a lot of nice data structures for queueing work and orchestrating programs in general.

Marcus21:12:03

Hi all! How would you do IPC in Clojure?

seancorfield22:12:26

@marcus.akre "It depends". Is this communication only ever going to be between two Clojure processes, or might other tech be used at any point for one or more of these processes?

Marcus22:12:38

clojure only.. on same machine

seancorfield22:12:44

There is no single "best" answer to how to do IPC in general but there are lots of possible solutions depending on what sort of trade offs you want to make. Is this bidirectional or more client/server? How robust does it need to be in the face of network issues? What about latency considerations? How large are the data packets you want to exchange? Maybe using a shared database and polling is a reasonable choice. Maybe using an external message queue service is a reasonable choice. Maybe using a REST-style API is appropriate.

seancorfield22:12:33

Since you say Clojure only on the same machine: are these processes in separate JVMs or could they both run within the same JVM? (then it wouldn't really be IPC and you could use any number of in-memory / in-process mechanisms)

Marcus22:12:50

I am not sure. If you start two separate java applications on the same host, will it then be the same JVM?

Marcus22:12:56

How could I access shared memory in such cases?

seancorfield22:12:50

Each Java application you start runs a separate JVM.

seancorfield22:12:19

I guess at this point, the question is more: what problem are you actually trying to solve here?

βž• 3
seancorfield22:12:18

(depending on exactly what problem you're trying to solve, maybe you could use a single JVM?)

Marcus22:12:56

I am trying to implement a light weight queue handler that other clojure programs can post jobs to

Marcus22:12:10

Of course, this can be solved in a number of ways.. ..even just by using existing software.

Marcus22:12:48

thanks! πŸ™‚

clyfe22:12:43

what is light weight about it? does the queue need to persist to survive restarts? does it have to ensure exact "execute once" semantics? is speed the main concern above the prior bits?

clyfe22:12:02

You do IPC same as in any other language, named pipe, socket etc

clyfe22:12:52

An interesting case (depending on security concerns) is to use a socket repl (prepl preferred) https://nrepl.org/nrepl/0.8/alternatives.html

clyfe22:12:45

Work being planted by sending a form to the worker process

seancorfield22:12:17

Using a prepl would be a nice Clojure-centric approach -- that thinks outside the box!

πŸ™‚ 3
seancorfield22:12:47

Although it is synchronous (unless you send futures).

Marcus22:12:59

It just needs to process jobs sequentially as they come in.. no persistence or execute once

seancorfield22:12:12

I guess it depends on what feedback you want to get from the process, if any.

Marcus22:12:16

I'll check prepl

Marcus22:12:11

I don't need any feedback from the process either

Dimitar Uzunov22:12:06

Not an expert on this topic, but I appreciate the discussion. Lately at work it seems like adding Redis or RabbitMQ seems to be the default overkill solution to any IPC..

seancorfield22:12:12

I'm curious as to what problem this solves that you couldn't solve with just executing code in a thread pool directly within each program? Using an agent, for example.

seancorfield22:12:42

(or a plain old Java executor -- or just using future directly)

Marcus22:12:53

@seancorfield I am trying to learn both Clojure and kind of Java at the same time. πŸ™‚ How can I utilize core.async or agent across separate clojure programs?

Marcus22:12:16

Because that was the first thing I was thinking about.

Marcus22:12:48

e.g. by separating things in libraries etc

πŸ˜† 3
seancorfield22:12:46

core.async and agent's are for in-process stuff.

seancorfield23:12:06

My question was: why write separate programs for this, at all?

seancorfield23:12:42

If it's just as a learning exercise, fair enough, but you need to figure out what you're trying to learn πŸ™‚ Is it about using sockets and reading/writing data on them? Is it about queuing? Or job control in general?

seancorfield23:12:22

What are the "jobs" that you want to be able to run? Just Clojure code expressions?

seancorfield23:12:20

How does Java fit into this? (even from a learning p.o.v.) That's a genuine question: I'm not sure what you'd be learning about Java in the scenario above...

Marcus23:12:56

It is a command handler for a CQRS system. So it is just Clojure code expressions. I want to put it into a separate program to be able to use it through different interfaces. E.g. via a web server and command line etc. I am not trying to learn Java really, but of course some Java concepts seeps through. :)

noisesmith23:12:38

@marcus.akre while it's trivial to send clojure code to another process (you can just start up a repl as a socket handler), I don't think it's a very good API for IPC

noisesmith23:12:03

for the same reason you wouldn't just use SSH and send shell commands