Fork me on GitHub
#beginners
<
2021-11-06
>
Jakub Zika07:11:39

Hi, is there some native function good for implementing rolling window?

Ben Sless07:11:03

partition-all I think

Jakub Zika07:11:19

@UK0810AQ2 This is what I mean by rolling window.

``````(loop [x [1 2 3 4 5 6 7 8 9 10]
window [0 0 0 0 0]]
(if (empty? x) (println window)
(do (println window)
(recur (rest x)
(-> (take 4 (drop 1 window))
vec
(conj (first x)))))))

\$ [0 0 0 0 0]
[0 0 0 0 1]
[0 0 0 1 2]
[0 0 1 2 3]
[0 1 2 3 4]
[1 2 3 4 5]
[2 3 4 5 6]
[3 4 5 6 7]
[4 5 6 7 8]
[5 6 7 8 9]
[6 7 8 9 10]
nil``````

Ben Sless07:11:54

https://clojuredocs.org/clojure.core/partition-all use partition-all with step 1 and concat the pad with the input sequence

Jakub Zika07:11:42

``````(partition-all 3 1 [1 2 3 4 5 6 7 8 9 10])
((1 2 3)
(2 3 4)
(3 4 5)
(4 5 6)
(5 6 7)
(6 7 8)
(7 8 9)
(8 9 10)
(9 10)
(10))``````

Jakub Zika07:11:14

And this gets me correct result. btw. partition (not partition-all) supports pad seq parameter, did you meant that? `(partition 5 1 (concat (repeat 5 0) [1 2 3 4 5 6 7 8 9 10]))`

Ben Sless08:11:55

In partition-all you can pass as an argument the (concat (repeat ,,,) xs)

Sebastian08:11:45

Hello. I am currently trying to split a 9*9 vector-matrix

``````[[5 3 4 6 7 8 9 1 2]
[6 7 2 1 9 5 3 4 8]
[1 9 8 3 4 2 5 6 7]
[8 5 9 7 6 1 4 2 3]
[4 2 6 8 5 3 7 9 1]
[7 1 3 9 2 4 8 5 6]
[9 6 1 5 3 7 2 8 4]
[2 8 7 4 1 9 6 3 5]
[3 4 5 2 8 6 1 7 9]]``````
into 9 squares. So first square would look like this
``````[[5 3 4]
[6 7 2]
[1 9 8]]``````
I've found this on stack overflow that basically does the same thing, https://stackoverflow.com/a/21391726/11109039 - And I have altered it to 3 instead of 2 squares. However, I am having a hard time understanding partial what going on. When I try to break it down I get too many or few arguments exceptions. Can anybody help break this down for me?

aqua09:11:06

`(partial apply map vector)` is like `#(apply map vector %)` (in the case of 1 argument). And for example `(apply map vector [[1 2 3] [4 5 6]])` is like `(map vector [1 2 3] [4 5 6])`. Does that help at all?

Mno09:11:13

I admit I didn't read everything but this sounds like you might want to look at `partition`

Sebastian10:11:02

Ah thank you Aqua, it was actually applying of the map and vector I had not understood. I think I might need to read up on apply

π 1
SK11:11:17

Why is the return value for (take 2 []) differ compared to (take-last 2 []) which is () vs nil? what's the rationale here?

tws12:11:24

`take` is lazy, `take-last` is not. The comments on the functions describe the βwhatβ pretty clearly, not sure on official rationale.

π 1
SK16:11:43

ok, so in general is it true, that everything which is lazy returns a seq (I guess so the lazyness is not broken), while everything which is not-lazy it depends ?

sova-soars-the-sora18:11:24

not-lazy means it tries to realize the sequence then.

sheluchin13:11:10

I want to qualify keywords to a particular namespace which I cannot require into my current namespace without causing a circular reference. Normally, I'd add it to the `:require` reference in `ns` with an alias and then do something like `::my-alias/foo`. Can I achieve something similar without creating a function for it? Is it unidiomatic to use the `alias` function in a file?

vanelsas13:11:50

I don't know your situation of course, but would it not be better to tackle the root cause (the circular reference)?

sheluchin13:11:24

I don't really need anything from the namespace other than qualified keywords, so there's not actually a circular reference problem.

delaguardo13:11:34

This link could be useful - https://clojure.atlassian.net/browse/CLJ-2123 There are some paths to go. And In clojure 1.11 you will be able to add namespace into require without loading it

β 1
π 1
pavlosmelissinos14:11:45

You can also use the full namespace instead of an alias @UPWHQK562.

sheluchin15:11:10

@UEQPKG7HQ yeah, that's what I'm trying to avoid... too lazy for all that typing π

pavlosmelissinos15:11:23

Like @U04V4KLKC said, 1.11 will solve your problem elegantly. Until then, I don't think it's unidiomatic to use the `alias` function. However, if your keyword namespaces match actual code namespaces and they happen to be so long that you have to use aliases, my first suggestion would be to consider using different namespaces for the keywords.

sheluchin12:11:34

com.example.model.foo is where my attributes are defined. Not the end of the world, but I like `::foo` much better than the fully qualified version. Not much wiggle room on that, unless I want to stop using reverse-domain name notation or go against Pathom's recommendations on namespacing :man-shrugging: I'll use `alias` until 1.11. Thanks @UEQPKG7HQ and @U04V4KLKC.

π 1
pavlosmelissinos14:11:08

> go against Pathom's recommendations on namespacing Do you have a link for that @UPWHQK562? I'd like to read more but can't find it on the official docs

sheluchin15:11:19

Mobile right now but I'll try to find relevant bits in docs later. Some of it might be from Wilker's talks. The gist is basically that you want universally unique keyword namespaces so that your application fits into the vision of the "maximal graph". Using reverse domain name notation is a good way to do that, as it gives your keywords contextual meaning in a universal way so they can be understood when placed beside keywords from other services. Using your own domain puts the keywords "in a namespace you control", which I think is recommended in the Fulcro or Pathom docs. Further, Fulcro RAD recommends defining your attributes in the com.example.model.foo namespace. Granted, the examples do not use fully qualified keywords - they use :foo/id instead of :com.example.model.foo/id - but I'm presuming that's just for brevity and the Pathom approach of universally unique keywords is still better for a production web app.

π 1
pavlosmelissinos19:11:13

Indeed, that sounds like a good practice to follow when designing public APIs

souenzzo12:11:32

@UPWHQK562 some comments: β’ The idea of use "universally unique keyword namespaces" actually is a clojure core idea. It comes from RDF inspirations and libraries like clojure.spec use/require these ideas to work well. β’ Pathom/Fulcro tutorials use short ns keywords, like `:user/id` just for easines of reading/writing/copy'n'paste.

wilkerlucio12:11:19

@UPWHQK562 hello, yes, long names are great. how long is still up to debate IMO, I personally think for public API's we should use at least 2 namespace segments (eg: `acme.user/name`, and more is always acceptable, having `:as-alias` available will be great and I hope that encourages people to use longer names

sheluchin13:11:40

@U2J4FRT2T @U066U8JQJ thanks for you comments and keeping me honest here π I think I really need to read more about RDF to understand these ideas at a deeper level. Also, on the multiple comments mentioning "public API", I recall I read somewhere (can't recall exactly where), that designing everything - public or private - with the mindset of it being public is a good idea. I guess from a practical point of view it can slow you down at times, but it sounds like good advice to me. Kind of unrelated, but it just feels wrong to me to use keyword namespaces that don't map directly to the filesystem.

Benjamin13:11:18

``````(doseq [id (:stuff config)]
(defmethod handle id [...] ...))``````
is good/bad to take some config and define toplevel functions based on that?

thom13:11:49

Not really bad, perhaps more idiomatic as a macro though.

π 1
Ben Sless13:11:06

Are there any other values you close over besides the id?

Benjamin13:11:11

@UK0810AQ2 no. Although now I realized if I want the exact behaviour for multiple things I can just make a generic label on the data instead

Ben Sless14:11:38

Exactly, you just need to close over the set of IDs or behaviors. Dynamically building multi methods based in input is scary.

Ben Sless14:11:16

And it's a way less visible solution

Benjamin14:11:19

what is a way to say `defmethod [:a <wildcard>]` ?

Ben Sless14:11:10

No, you need the methodical library

Ben Sless14:11:56

Or you could hierarchically define another multi method, which will be pretty gross

π 1
Ben Sless14:11:45

``````(defmulti outer (fn [[& args]] (first args)))
(defmulti inner (fn [[& args]] (first args)))
(defmethod outer :a [_ & args] (apply inner args))
(defmethod inner :default [_ & args] ,,,)``````

Ben Sless14:11:11

That works, but I wouldn't like having to work with it long term

π 1

Hey is there a way to get the repl to render newlines instead of `\n`? (when constructing a string)

seancorfield19:11:04

You can print the last result:

``````user=> (str "two\nlines")
"two\nlines"
user=> (println *1)
two
lines
nil
user=>``````

To get this working in nrepl/cursive I set `*print-readably*` to false, then I could print ascii art π I did this with (binding) not sure how to reset it globally

Andrew Byala22:11:48

Hey, everyone. I've been tinkering with Clojure for about two years, mostly for Advent Of Code, and I'm trying to write a full program now for fun. I've pulled in Integrant just fine, but I have a more general question about where to store some aspect of my state. The program is a mostly batch process (for now) that periodically interrogates several ActiveMQ brokers, looks at their statistics, and tries to decide if something looks wrong; this could include consumers falling behind producers, messages in DLQs, etc. My integrant code configures my connections to the brokers, ports, the list of analyzers I want to apply, and the background processing configuration (how much time to wait before inspecting the data again). My question is - some of my analyzers are inherently stateful. For instance, one of them records the oldest message ID in the queue, and if it isn't picked up for some amount of time, then it reports a problem. I don't think this data is correct to store explicitly in my Integrant state; the list of analyzers is configurable in Integrant, but the running data doesn't seem to belong there. I figure I can handle this state in at least one of two ways: 1. The Integrant component that sets up the lookup logic can hold some global atom that maps each analyzer to some map that it wants to handle. When an analyzer looks at some new data, it returns back its new state, and the application components swaps it into the global atom. 2. Each analyzer can "privately" hold its own atom of its internal state somehow, perhaps in a constructed `defrecord`, since its internal cache is nobody's business but its own. Option 2 is correct in OO (I'm a Java programmer by trade), but that means the function to analyze some new ActiveMQ data impure because it results in a side effect within the analyzer itself. Option 1 keep more of my functions pure, but it necessarily makes my top-level program responsible to store the state of each analyzer, which seems like some weird form of coupling. Am I write that Option 1 is more Clojure-y and functional? Is this how a "functional core, imperative shell" is supposed to look?

seancorfield22:11:27

@abyala If an analyzer is inherently stateful, I think it's okay for it to close over some state when it is "started", e.g.,

``````(defn init-analyzer-a [opts]
(let [state (atom nil)]
(fn [input]
... use state to produce output ...)))``````

seancorfield22:11:14

Then you call `(init-analyzer-a {:some "options"})` and you get back a stateful analyzer that behaves like a regular function except it has mutable state locked into it.

seancorfield22:11:51

After all, some of Clojure's own transducer functions are stateful and when you invoke them, you get back a function that has closed over some state. Take a look at `(source partition-all)`, for example, which closes over a mutable Java `ArrayList`.

Andrew Byala23:11:42

Thanks, @seancorfield; that helps a bunch. That also answers my other question about how to initialize that state without necessarily having to pass it in - my application state holds on to the function that's created from the `init-analyzer-a` function all up-front. I was erroneously looking at `defrecords` as a way to hold onto "local" state, but that didn't make sense without a function to kick it off. Thank you!

seancorfield23:11:24

Records are rarely needed. Stick to plain maps where you can. This is still good guidance about types in Clojure: https://cemerick.com/blog/2011/07/05/flowchart-for-choosing-the-right-clojure-type-definition-form.html -- although now you can extend protocols via metadata on values so you need records even less, in my opinion.

seancorfield23:11:30

It can be very hard to shake off OO thinking when you first start learning Clojure and FP, so I try to steer folks away from records and "traditional" state if possible π

Andrew Byala23:11:12

Ha! Very old habits die hard. Thanks, I'll check out the link!