beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
João Galrito 2020-09-24T00:13:35.075800Z

someone the other day mentioned defsomething macros are an anti-pattern

João Galrito 2020-09-24T00:13:42.076Z

could someone explain why

alexmiller 2020-09-24T00:16:07.076300Z

I don't think they're an antipattern

alexmiller 2020-09-24T00:16:31.076400Z

¯\(ツ)

João Galrito 2020-09-24T00:16:35.076600Z

hehe

alexmiller 2020-09-24T00:17:06.077400Z

I mean.... core has defmacro, defmulti, defmethod, defrecord, etc

João Galrito 2020-09-24T00:17:21.077900Z

(defmacro def-effect [name args & body]
  `(defn ~name ~(conj args ~'optional) ~@body))
(defeffect draw [amount] nil)

(defn effect [f] (fn [optional & args] (apply f args)))
(def draw (effect (fn [amount] nil)))

alexmiller 2020-09-24T00:17:32.078400Z

anytime you have some sort of complex thing that results in a var definition, that's a reasonable candidate

João Galrito 2020-09-24T00:17:40.078600Z

i want to wrap an "effect" into something that handles an extra optional argument

João Galrito 2020-09-24T00:18:26.079500Z

they are not equivalent and not even correct right now

João Galrito 2020-09-24T00:18:33.079700Z

but just for the sake of demonstration

alexmiller 2020-09-24T00:19:10.080100Z

I'm not sure you're getting enough value out of this to be worth doing, but this seems fine imo

alexmiller 2020-09-24T00:19:22.080300Z

as an idea

João Galrito 2020-09-24T00:23:09.080800Z

probably not

João Galrito 2020-09-24T00:23:39.081200Z

I might just have an optional function and call (optional (draw 1))

Nazar Trut 2020-09-24T00:51:11.081400Z

@joao.galrito

Nazar Trut 2020-09-24T00:51:22.081600Z

What is a way that i can check if there is two "nots" in my sequence like this '(not (not b)), so therefore, if there is two "not" like this example. (not(not a)) would be true (not a) would be false

Nazar Trut 2020-09-24T00:51:37.082Z

I know @seancorfield helped me earlier but i still dont get how i cant do this

seancorfield 2020-09-24T01:17:45.083200Z

Here's what I said: I would define a predicate that returned true if an expression was `(not X)` and then you can do `(and (has-not? expr) (has-not? (second expr)))` if you make `has-not?` robust (check for sequential expr and then check first item is `'not`) -- that's pretty much giving you all the code so maybe you can take another look and see if you can figure it out @ntrut?

seancorfield 2020-09-24T01:18:44.084100Z

This is about solving a bigger problem (checking for two "nots") by breaking it down into smaller problems (checking if an expression starts with not)

seancorfield 2020-09-24T01:21:13.086100Z

has-not? would return true for both of those expressions, and (second expr) would be (not a) for the first one and a for the second one -- so if you then call has-not? on each of those you get true for (not a) and false for a -- hence (and (has-not? expr) (has-not? (second expr))) provides the result you want: true for (not (not a)) and false for (not a)

seancorfield 2020-09-24T01:21:23.086400Z

Does that make sense @ntrut?

seancorfield 2020-09-24T01:22:52.087400Z

You've already written code that is the same body of the has-not? function -- (and (sequential? expr) (= 'not (first expr)))

Nazar Trut 2020-09-24T01:23:29.087900Z

Ok thank you so much, this makes a little more sense, ima see what i can do

João Galrito 2020-09-24T01:34:17.089Z

or you can do (= 2 (count (filter #(= 'not %) (flatten kb)))) 😄

João Galrito 2020-09-24T01:34:46.089600Z

just kidding

Nazar Trut 2020-09-24T01:35:01.089900Z

i tried that but my only problem is that if i have (not(if (not a)(not b))

Nazar Trut 2020-09-24T01:35:09.090200Z

it would count 3

Nazar Trut 2020-09-24T01:35:37.090500Z

I need to do double negation, so if (not(not X)

Nazar Trut 2020-09-24T01:35:42.090700Z

would derive X

seancorfield 2020-09-24T01:39:52.091600Z

Writing small predicates that you can easily combine will make it easier to match on more complex expressions.

seancorfield 2020-09-24T01:40:30.092200Z

@joao.galrito flatten is almost always the wrong answer 😆 (to any question)

João Galrito 2020-09-24T02:17:11.093300Z

@seancorfield I actually just used it for something 😛

seancorfield 2020-09-24T03:27:08.094300Z

@joao.galrito FWIW, we have 9 occurrences of flatten in our codebase at work (over 100K lines) but I think most of those could be replaced (and probably should be 🙂 )

👍 2
João Galrito 2020-09-24T14:04:35.008Z

In my case i wanted to flatten a nested lazy seq of strings into a single seq to send to an InputStream, so I think it's a good use case 😛

seancorfield 2020-09-24T15:36:01.038Z

mapcat would likely be better (usually folks use flatten when they really only need a single level collapsed -- which is what mapcat does).

seancorfield 2020-09-24T03:42:57.095Z

@murala_anilrao Do not cross-post questions in multiple channels. I have deleted it from #clojure

👍 1
seancorfield 2020-09-24T03:43:52.000800Z

You need to provide a lot more detail about what you've tried, what libraries you are using, what worked and what didn't work, and what failures you got.

👍 1
seancorfield 2020-09-24T03:47:09.002400Z

I also see you've repeatedly posted this exact same question without any useful details in multiple channels over the last few days, even after people have offered suggestions. If you continue that behavior, you will be ejected from this community. That is not acceptable behavior here.

👍 1
AM 2020-09-24T03:54:31.003100Z

apologies for that

Mark Wardle 2020-09-24T06:15:45.003400Z

See https://aleph.io/examples/literate.html#aleph.examples.websocket But you can use sente as both a clj client and server as well.

2020-09-24T13:26:42.005700Z

In Clojue, we can create keyword with the keyword function. For example we could create the following one: (keyword "a/b" "c/d"). How des clojure track the name and the namespace in this case?

alexmiller 2020-09-24T13:27:34.006200Z

in that case you've built a keyword that cannot be printed and re-read

alexmiller 2020-09-24T13:27:45.006500Z

b/c it violates the keyword naming conventions

alexmiller 2020-09-24T13:28:22.007300Z

but keywords are composed of independent namespace and name fields so you could still extract those parts programmatically (with namespace and name functions)

alexmiller 2020-09-24T13:28:50.007600Z

and to head off the inevitable question https://clojure.org/guides/faq#unreadable_keywords

👍 2
Jim Newton 2020-09-24T14:16:19.014200Z

In Common Lisp I can write a lambda form like the following:

(lambda (a b c d)
   (declare (type number a c)
            (type integer  c)
            (type string d))
   ...)
And depending on which compiler I'm using, the compiler is allowed to optimize the code in specific ways. I can also write a macro to implement my own version of lambda such as
(my-lambda (a b c d)
   (declare (type number a c)
            (type integer c)
            (type string d))
   ...)
and in the macro, examine the declarations (as they are just raw s-expressions given to the macro as input). The advantage being that every CL user automatically knows how to use my-lambda because I'm promising that it has the same syntax as lambda. QUESTION: is there any sort of type optional type declaration which I can use in my macro which corresponds to something the language already supports? I don't want to invent my own if there is already one. SUGGESTION: I know that spec can be used as sort-of declarations, but I don't want my macro to have to implement an exhaustive spec parser. I've seen some code using java-hints but I would need to be able to parse them in my macro code.

sova-soars-the-sora 2020-09-24T14:21:36.017600Z

sanity check.. What does this mean?

java.lang.ClassCastException: class java.lang.String cannot be cast to class clojure.lang.IFn

pavlosmelissinos 2020-09-24T14:22:36.017800Z

Probably incorrect argument order? Wild guess: did you happen to use a -> instead of a ->> (or vice versa)?

vncz 2020-09-24T14:23:48.018200Z

I'd say a function argument is expected to be a function but you've passed a string

👍 2
vncz 2020-09-24T14:24:41.018600Z

Repro:

vncz 2020-09-24T14:24:56.018800Z

sova-soars-the-sora 2020-09-24T14:28:14.019300Z

Aha, thank you ^_^

Jim Newton 2020-09-24T14:52:03.021400Z

Is there a function in the clojure lib which I should use to break up an input sequence into a sequence of sequences, each of a specified length. I.e., break up a long list into lists of length 3? (a b c 1 2 3 10 20 30... ) -> ((a b c) (1 2 3) (10 20 30) ...)

Darin Douglass 2020-09-24T14:52:55.021700Z

(partition 3 my-seq)

Darin Douglass 2020-09-24T14:54:32.023300Z

note: partition will drop the last elements of the seq if they can't be made into a group of n. use partition-all if you needed all elements

👍 2
🙌 1
Darin Douglass 2020-09-24T14:54:59.023600Z

user=> (partition 3 (range 10))
((0 1 2) (3 4 5) (6 7 8))
user=> (partition-all 3 (range 10))
((0 1 2) (3 4 5) (6 7 8) (9))

Jim Newton 2020-09-24T15:05:13.025600Z

thanks, there are lots of partitioning functions, and their names are different in every programming language. What is the name of the function which takes a sequence and a delimiter and splits the sequence into subsequences at that delimiter. (split-on-delimiter '& [a b c & d e f g]) --> ((a b c) (d e f g))

2020-09-24T15:08:17.026300Z

partition-by is close, but not exactly that. You could probably use partition-by and then something like (map rest ...) on the result.

Jim Newton 2020-09-24T15:08:48.027200Z

(split-with (fn [x] (= x '&)) '(a b c & d e f))
returns the following, which surprises me: [() (a b c & d e f)]

2020-09-24T15:09:24.027800Z

Note: I did not have that memorized, but the Clojure cheatsheet has most of the Clojure core functions organized by purpose/behavior, and I found partition and partition-by , and others, in the "Seq In, Seq Out" section: https://clojure.org/api/cheatsheet

2020-09-24T15:10:21.028800Z

And each of the functions links to http://ClojureDocs.org where there are community-contributed examples of use of most functions, sometimes pointing out corner cases/gotchas that are not always immediately obvious from the doc string.

Jim Newton 2020-09-24T15:14:39.029200Z

(partition-by (fn [x] (= x '&)) '(a b c & d e f)) --> ((a b c) (&) (d e f))

Jim Newton 2020-09-24T15:14:46.029400Z

that's pretty close.

2020-09-24T15:15:15.029600Z

user=> (def d1 '[a b c & d e f & g h])
#'user/d1
user=> (def d2 (partition-by (fn [x] (= x '&)) d1))
#'user/d2
user=> d2
((a b c) (&) (d e f) (&) (g h))
user=> (remove (fn [l] (= '& (first l))) d2)
((a b c) (d e f) (g h))

Jim Newton 2020-09-24T15:16:40.030800Z

as I'm trying to parse a lambda list, there should be a maximum of 1 &. What does the following mean? Does it have a meaning? (fn [a b c & d & e] ...)

2020-09-24T15:17:15.031400Z

I would hope it gives an error, but it might just silently do weird things.

Jim Newton 2020-09-24T15:17:34.031600Z

good! it indeed gives an error.

Jim Newton 2020-09-24T15:18:04.032200Z

so I can just assert beforehand that there exists maximally 1 & in the seq

alexmiller 2020-09-24T15:18:16.032400Z

that's part of destructuring spec

2020-09-24T15:18:57.033400Z

And that & is not last, which Clojure spec contains code for checking, and leads to the error you see when you try to define such a function.

alexmiller 2020-09-24T15:19:20.033900Z

I mean, if you're parsing destructuring you could just use the spec and s/conform to conform the spec and get data

alexmiller 2020-09-24T15:19:39.034200Z

depending on your goals

Jim Newton 2020-09-24T15:20:14.035200Z

(let [[prefix _ suffix] (partition-by (fn [x] (= x '&)) lambda-list)] ... I now need to verify that suffix is either empty or a singleton

Jim Newton 2020-09-24T15:20:56.036500Z

alex, your suggestion is what exactly? Can I call a particular spec function to parse this lambda list for me and give me back a data structure I can use?

2020-09-24T15:21:10.036900Z

Regarding your earlier question about declare in Common Lisp, the closest similar-but-not-the-same things in Clojure I can think of are type hints, and things like spec and Plumatic schema. None are exactly like what you are asking for, I don't think.

neilyio 2020-09-24T15:29:07.037900Z

What's the story with the -S in a command like this? Why do we need it?

clj -Sdeps '{:deps {bidi {:mvn/version "2.1.6"}}}'

alexmiller 2020-09-24T15:45:21.039400Z

many of the settings have -S at the front

neilyio 2020-09-24T15:47:44.040100Z

I'd just love to know the reasoning, it helps me remember details like this if I know why they're there. Is it a Java interop thing?

2020-09-24T16:04:22.041500Z

The choice of -S is not influenced by Java interop, that I am aware of. The java command has its own slew of command line options, and I don't think -S is one of them. Is your question "Why S, and not some other letter"?

2020-09-24T16:05:30.042100Z

Or maybe your question is "Why -Sdeps and not -deps ?"

neilyio 2020-09-24T16:06:17.043Z

Both! Why is there a letter at all?

2020-09-24T16:06:40.044300Z

I mean, 'd' is a letter.

neilyio 2020-09-24T16:06:42.044400Z

Do -S -A etc. have particular meanings?

seancorfield 2020-09-24T16:07:04.044900Z

I always took -S to indicate "Here's a script argument" to distinguish -Sdeps, -Stree, -Spom, etc from execution-related stuff (the various alias signifiers).

seancorfield 2020-09-24T16:07:59.045900Z

-M for aliases and :main-opts, -X (in the prerelease) for aliases and :exec-fn, -A for "all" originally but now more "aliases for REPL"

seancorfield 2020-09-24T16:08:25.046400Z

There are also -R for resolve aliases and -C for classpath aliases.

seancorfield 2020-09-24T16:09:48.048Z

I think the CLI is pretty consistent about using single uppercase letters to introduce its own options, and any lowercase one would be passed on to the program being run (`clojure.main` understands -i, -e, -m, -r; other "main" functions may understand other options).

seancorfield 2020-09-24T16:10:30.048400Z

Does that help make sense of it all @neil.hansen.31?

neilyio 2020-09-24T16:12:13.049900Z

@seancorfield That's massively helpful, thank you. I guess I've been thinking the command line would be "prettier" without those prefixes, but I'm glad to know what they are now. Your note about the lower-case flags is also very enlightening.

neilyio 2020-09-24T16:13:01.050900Z

Also, this has been a big "I really don't know how Clojure works" day for me so far, and I've been through a whole bunch of your blog posts. Thanks for all the knowledge you've shared.

seancorfield 2020-09-24T16:14:25.051400Z

Happy to help! I'm always pleased to see more folks using Clojure in general and the CLI/`deps.edn` in particular.

seancorfield 2020-09-24T16:15:58.052800Z

(hence my dot-clojure repo https://github.com/seancorfield/dot-clojure and also my Atom/Chlorine setup https://github.com/seancorfield/atom-chlorine-setup that supports working with a Socket REPL started from the CLI and with either Cognitect's #rebl or @vlaaad’s #reveal browser/visualization tools)

practicalli-john 2020-09-24T16:18:24.053300Z

Sounds like you have it. Just to elaborate a little. IFn is the interface for functions, so when ever a value is used as a function call, you will see that IFn error. For example (1 2 3) cause the error as Clojure tries to evaluate 1 as the function with 2 & three its arguments. 1 is not defined as a function, so the error message is shown.

Test This 2020-09-24T16:21:34.055900Z

I am trying to use amazonica to work with aws s3. With the following command I can successfully upload the file.

(ns ...
  (:require [amazonica.aws.s3 :as s3]
   .
   .
)

(s3/put-object aws/cred {:bucket-name aws/mybucket 
                                      :key keyval
                                      :file fileobj})
This, however, loads the file as a private file. How do I set the acl to public-read. Here on Stackoverflow (https://stackoverflow.com/questions/6524041/how-do-you-make-an-s3-object-public-via-the-aws-java-sdk) there is a question asking how to do this with Java. And the answer says:
return s3Client.putObject(
   new PutObjectRequest(bucketName, objectKey, inputStream, metadata)
      .withCannedAcl(CannedAccessControlList.PublicRead));
Can someone please point out how I can translate it to use with amazonica in Clojure?

2020-09-24T16:25:16.056100Z

I believe he means something like this. These examples use the same specs that Clojure itself uses to give the error message for several ill-formed attempts at writing defn forms, including having multiple &, or & at the end of a parameter list:

;; The specs mentioned below in the namespace clojure.core.specs.alpha
;; are defined in this file:
;; <https://github.com/clojure/core.specs.alpha/blob/master/src/main/clojure/clojure/core/specs/alpha.clj>

user=&gt; (require '[clojure.spec.alpha :as s])
nil
user=&gt; (require '[clojure.core.specs.alpha :as cspecs])
nil
user=&gt; (pprint (s/conform ::cspecs/defn-args (rest '(defn foo [x &amp; y] (list x y)))))
{:fn-name foo,
 :fn-tail
 [:arity-1
  {:params
   {:params [[:local-symbol x]],
    :var-params {:ampersand &amp;, :var-form [:local-symbol y]}},
   :body [:body [(list x y)]]}]}
nil

user=&gt; (pprint (s/conform ::cspecs/param-list '[x &amp; y]))
{:params [[:local-symbol x]],
 :var-params {:ampersand &amp;, :var-form [:local-symbol y]}}
nil

2020-09-24T16:26:22.056400Z

There is a lot more detail about spec that can be found here: https://clojure.org/guides/spec

Jim Newton 2020-09-24T16:37:18.056600Z

I've copied this question https://clojureverse.org/t/expected-syntax-for-destructuring-bind/6593 hoping there is more discussion.

Jim Newton 2020-09-24T16:39:02.056900Z

I copied that discussion https://clojureverse.org/t/expected-syntax-for-destructuring-bind/6593 for further elaboration.

2020-09-24T16:44:48.057200Z

cool thanks for your dot-clojure link! Quick question: reading through the deps.edn there, all the packages referenced have a {:mvn/version "RELEASE"} does that grab the latest package version, or does RELEASE need replacing with a specific version? I don't know maven very well.

seancorfield 2020-09-24T16:47:52.057400Z

As it says in the README, many of those aliases fetch the latest stable release of a tool -- which is what "RELEASE" does.

seancorfield 2020-09-24T16:48:35.057600Z

You shouldn't use it for anything other than dev/test tools because you won't get a specific version and it's better for project dependencies to only rely on fixed versions.

2020-09-24T16:49:53.057800Z

Understood, and I prefer specifying for actual releasable projects too, but this perfect for all the tools in the global deps.edn thanks!

seancorfield 2020-09-24T16:50:26.058Z

There's also "LATEST" which will include snapshots.

seancorfield 2020-09-24T16:50:41.058200Z

(but I think they're both technically deprecated, even in Maven)

2020-09-24T16:55:01.058400Z

ah, good to know. I don't suppose there's a tool to update the specific versions in a deps.edn on command, in case one wanted to update one's project dependencies, or at least a diff of what's different?

seancorfield 2020-09-24T17:06:19.058600Z

Several -- see https://github.com/clojure/tools.deps.alpha/wiki/Tools#deps-management

seancorfield 2020-09-24T17:06:54.058800Z

I would never let a tool automatically update my deps.edn file, but I do use tools to let me know about newer versions.

2020-09-24T17:40:03.059Z

yep, agree. Thanks again!

João Galrito 2020-09-24T18:13:31.059800Z

what is the difference between concat and lazy-cat ? if I want to concatenate 2 lazy seqs into 1 lazy seq, I should use lazy-cat right? will concat realize its arguments imediately?

João Galrito 2020-09-24T18:14:44.060400Z

(apparently not, as I just did (take 10 (concat (repeat 10))) and worked as expected

seancorfield 2020-09-24T18:14:50.060600Z

doc tells you the difference:

user=&gt; (doc concat)
-------------------------
clojure.core/concat
([] [x] [x y] [x y &amp; zs])
  Returns a lazy seq representing the concatenation of the elements in the supplied colls.
nil
user=&gt; (doc lazy-cat)
-------------------------
clojure.core/lazy-cat
([&amp; colls])
Macro
  Expands to code which yields a lazy sequence of the concatenation
  of the supplied colls.  Each coll expr is not evaluated until it is
  needed. 

  (lazy-cat xs ys zs) === (concat (lazy-seq xs) (lazy-seq ys) (lazy-seq zs))
nil
user=&gt; 

seancorfield 2020-09-24T18:15:50.061200Z

concat itself is already lazy. lazy-cat is a shorthand for applying lazy-seq to each thing you are concatenating.

João Galrito 2020-09-24T18:18:51.061400Z

thank you

João Galrito 2020-09-24T18:23:14.062Z

is there something like pjuxt?

dpsutton 2020-09-24T18:29:31.062300Z

What is pjuxt?

João Galrito 2020-09-24T18:30:16.062900Z

juxt but parallel 😛 applies the same argument to a number of functions

João Galrito 2020-09-24T18:30:26.063100Z

and returns a vec with the results

João Galrito 2020-09-24T18:32:38.064200Z

(defn pjuxt [fns] (fn [&amp; args] (pmap #(apply % args) fns)))

2020-09-24T18:46:35.067100Z

There are very few functions in Clojure's core that cause things to happen in parallel. Maybe a dozen or two? pmap, future, agent-related things, the reducers library ... I am probably missing a couple, but not very many. No pjuxt for sure.

2020-09-24T18:49:19.068900Z

Of course opinions differ on what is generally useful and "ought to be in core", but the tendency is towards general building blocks. As your code example shows above, the ones provided make pjuxt pretty short to write.

2020-09-24T18:49:55.069300Z

the ones that exist are problematic

2020-09-24T18:50:04.069600Z

pmap is not great

João Galrito 2020-09-24T18:50:31.069800Z

why?

Michael J Dorian 2020-09-24T18:51:47.070800Z

For the majority of cases you would probably want to use something like this instead:

(defn ppmap
  "Partitioned pmap, for grouping map ops together to make parallel
  overhead worthwhile"
  [grain-size f &amp; colls]
  (apply concat
   (apply pmap
          (fn [&amp; pgroups] (doall (apply map f pgroups)))
          (map (partial partition-all grain-size) colls))))
(time (dorun (ppmap 1000 clojure.string/lower-case orc-name-abbrevs)))

2020-09-24T18:52:08.071300Z

the way it combines parallel computation with lazy-seqs can cause non-obvious behaviors

Michael J Dorian 2020-09-24T18:52:19.071600Z

source: https://www.braveclojure.com/zombie-metaphysics/ The standard version is a building block for most things, and it can still be very useful if you have a small number of large tasks

2020-09-24T18:52:57.073Z

so for example, your pjuxt will behave differently depending on the type of the collections of fns you pass in

2020-09-24T18:53:09.073400Z

If your goal is to maximize parallel computation, pmap can often fall well below that if different elements require significantly different amounts of time to calculate, because pmap does not work more than some small finite number of elements ahead of the earliest one that is incomplete.

2020-09-24T18:53:13.073600Z

(really it depends on how the seq is constructed)

2020-09-24T18:54:30.076200Z

Commonly suggested alternatives, that give you more control, are the claypoole library https://github.com/TheClimateCorporation/claypoole , or just using Java's ExecutorService framework and threads directly via Java interop calls.

2020-09-24T18:54:46.076700Z

using something like (mapv #(future ...) ...) is going to behave much more obviously

João Galrito 2020-09-24T18:54:51.076800Z

imagining I want to do some processing on an infinite seq, but I want to process the same seq in 3 different ways in parallel

João Galrito 2020-09-24T18:54:59.077Z

what would be a good approach?

2020-09-24T18:56:19.077700Z

Do you care whether one of the 3 different ways gets arbitrarily far ahead in the infinite seq versus the other ways?

2020-09-24T18:56:32.078100Z

infinite seqs like that are almost always a bad idea

2020-09-24T18:56:44.078600Z

Or do you want them to stay reasonably close to each other in where they are working in the seq?

2020-09-24T18:56:54.078900Z

because they are usually used to model something like pulling messages off a queue as an infinite seq

2020-09-24T18:58:03.080300Z

and that is an overly simplified model of interacting with a queue (assuming the queue is a service, what if there is a network error, etc)

João Galrito 2020-09-24T18:58:15.080500Z

@andy.fingerhut the second option I guess, for memory reasons

2020-09-24T18:58:28.081Z

and it means you have this lazy thing, that you cannot consume without potentially blocking for io

João Galrito 2020-09-24T18:59:43.082600Z

@hiredman I understand. I'm learning so I'm going for a naive approach, and refine it as necessary

2020-09-24T18:59:57.083100Z

so you have this lazy thing, which is a paradigm that works best when you don't have to care about when some computation happens, combined with operations you usually end up caring a lot about when they happen (io)

João Galrito 2020-09-24T19:00:08.083400Z

I'm quite green irt concurrency and parallelism

2020-09-24T19:01:41.084900Z

well, I mean, you wouldn't be the first to use infinite lazy seqs like that, it is pervasive, just not great

💯 1
João Galrito 2020-09-25T12:40:07.096800Z

what are the advantages of that approach?

2020-09-25T16:03:07.102300Z

the concurrency stuff is exposed and you don't get weird pmaps weird laziness + concurrency

João Galrito 2020-09-28T02:04:14.213Z

I understand that it gives me more control, and is probably better to have things being more explicit, but the seq abstraction feels so appropriate to use here... I'm moving streams of data between multiple databases, queues, and processing pipelines and makes it so straightforward to reason with

João Galrito 2020-09-28T02:04:42.213200Z

I have my functions that operate on a single object and then weave the seqs through them

João Galrito 2020-09-28T02:07:30.213400Z

I understand that it might bite me in the ass later, but at the same time it's fine if it breaks because of connection errors, timeouts or such. If that happens then we'll probably have bigger problems, and I'm building this in a way that it can resume its work quickly after a crash

João Galrito 2020-09-24T19:02:26.085400Z

yea, what I'm trying to do is subscribe to a queue and do some processing on each message

João Galrito 2020-09-24T19:03:04.086500Z

and that processing is (in this case) 3 independent processing tasks

2020-09-24T19:05:46.088100Z

the big question is output, what result do you want? do you care about the result of the tasks? does the output need to be synched(for a given input, the three outputs are grouped together)?

João Galrito 2020-09-24T19:20:26.088300Z

it doesn't need to be synced

João Galrito 2020-09-24T19:21:21.089100Z

but I suppose they shouldn't be too far from each other because it will hold more elements of the seq in memory

João Galrito 2020-09-24T19:21:34.089400Z

the processing will be identical in the 3 threads though

João Galrito 2020-09-24T19:21:51.089700Z

it's just based on different properties of the object being processed

sova-soars-the-sora 2020-09-24T21:23:41.090500Z

Thank you

João Galrito 2020-09-24T22:07:41.090800Z

what would you say is a better approach?

2020-09-24T22:35:49.091700Z

I would maybe do something like creating an executor with a threadpool of size N, then putting a task on that executor that pulls from the queue, submits a task for the each of the things you want to do with what you pulled from the queue to the same executor, then submits the original pulling from the queue task again

2020-09-24T22:36:40.091900Z

the tricky thing with that is plumbing return values out, but if you don't care about them then it works well