This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-05-28
Channels
- # babashka (29)
- # beginners (179)
- # bristol-clojurians (1)
- # calva (9)
- # chlorine-clover (47)
- # cider (57)
- # clj-kondo (1)
- # cljs-dev (13)
- # clojure (241)
- # clojure-europe (9)
- # clojure-nl (4)
- # clojure-norway (88)
- # clojure-spec (4)
- # clojure-uk (15)
- # clojurescript (211)
- # clojutre (1)
- # community-development (8)
- # core-async (1)
- # datomic (31)
- # figwheel-main (33)
- # fulcro (29)
- # ghostwheel (6)
- # graalvm (11)
- # graphql (12)
- # instaparse (4)
- # jobs (1)
- # jobs-discuss (17)
- # leiningen (7)
- # malli (6)
- # meander (38)
- # off-topic (208)
- # onyx (6)
- # re-frame (23)
- # reagent (8)
- # shadow-cljs (61)
- # spacemacs (10)
- # sql (5)
- # yada (5)
(into
[]
(map (fn [x]
(if
(and (vector? x) (< 2 (count x)))
;if true, transform here
;grab first and last
(let [tag (first x)
last-item (last x)]
;build the transformed vector
[tag
;the new string
(str
last-item
" of the "
(->> x
(drop 1)
(drop-last)
(reverse)
(reduce
(fn [accum item]
(if (vector? item)
(conj accum (second item))
(conj accum item)))
[])
(interpose " of the ")
(vec)
(apply str)
))])
;else return unchanged
x))
[:S
[:ni-jar 'sensei]
[:ga-jar
[:no-jar 'gakkou]
[:no-jar 'sensei]
'hanashi]
[:verb 'sabishii]]))
=> [:S [:ni-jar sensei] [:ga-jar “hanashi of the sensei of the gakkou”] [:verb sabishii]]
You are incredible! whaaat let me take a look at this
it is somewhat simplistic, and I’m not sure it covers all cases, but whatever we talked about it does… it is very simplistic in the sense that the way it checks whether to transform is via (and (vector? x) (< 2 (count x)))
But I went by your phrase “Essentially any vector that has more than two elements [:key and val] needs to be smashed to have just key and val.”
So for your data structure, at this limited level of nesting, that should cover the requirement…
that's very good. i'm trying to do it in cljs and it is complaining there is no rseq? ... hmmm. i very much appreciate your help & efforts!
(let [transformer
(fn [x]
(let [tag (first x)
last-item (last x)]
[tag
(str
last-item
" of the "
(->> x
(drop 1)
(drop-last)
(reverse)
(reduce
(fn [accum item]
(if (vector? item)
(conj accum (second item))
(conj accum item)))
[])
(interpose " of the ")
(vec)
(apply str)
))]))]
;run it
(into
[]
(map (fn [x]
(if
(and (vector? x) (< 2 (count x)))
;transform here
(transformer x)
x)))
[:S
[:ni-jar 'sensei]
[:ga-jar
[:no-jar 'gakkou]
[:no-jar 'sensei]
'hanashi]
[:verb 'sabishii]]))
that is amazing
and quite clean. thank you. i gotta take a good look at it
There is one more thing
it can be :no-jars or :to-jars .. they work the same way, but don't mix.
you got it though
i can probably add those conditionals
like if there is more than one :no-jar per squad, i can run the one transform, and if there is more than one :to-jar i can run a different transform, but all i need to do is make your one transformer into two
Let me give you an example
I guess it's ok if they mix, but the :to-jars should render to a different string:
input:
[:S [:ga-jar [:to-jar hakujo] [:to-jar yata] seuss] [:verb iru]]
desired output:
[:S [:ga-jar "hakujo and yata and seuss"] [:verb iru]]
So if there were a way to distinguish transformers that would be cool
Thanks for your patience 🙂 my brain is mostly melty today
(let [transformer
(fn [x]
(let [tag (first x)
last-item (last x)
any-jar (-> x (second) (first))
concat-str (condp = any-jar
:no-jar " of the "
:to-jar " and "
" hmmm ... ")]
[tag
(str
last-item
concat-str
(->> x
(drop 1)
(drop-last)
(reverse)
(reduce
(fn [accum item]
(if (vector? item)
(conj accum (second item))
(conj accum item)))
[])
(interpose concat-str)
(vec)
(apply str)
))]))]
(into
[]
(map (fn [x]
(if
(and (vector? x) (< 2 (count x)))
;transform here
(transformer x)
x)))
[:S
[:ni-jar 'sensei]
[:ga-jar
[:to-jar 'gakkou]
[:no-jar 'sensei]
'hanashi]
[:verb 'sabishii]]))
It starts to smell in the sense that now you’re expecting a certain shape of your data structure… if it’s guaranteed to be this shape, then OK… but if it’s not… but it does work for this specific case
That's really wonderful, yeah it's pretty much guaranteed to be this way
the data shape i mean
Again, a lot of assumptions here for the data shape… if you get something in a weird shape, chances are this will throw an error or not work as expected
the interface that generates the data is buttons you click, so it's very hard to evade the rules :2
Ideally, I would find a way to group things by :to-jar or :no-jar so that keyword appears once, rather than just grabbing the second item of a vector and then looking at the first item of that sub-vector
Are there patterns for threads (actually processes) updating an atom when they finish? After the update is finished it should trigger some function. Is the best way to let the process know about the data structure it should update? And then have a watcher on the data structure which triggers the function? I'm sure I could remove some complection with channels, but it makes everything non-deterministic.
Yes, thanks 🙂 But I am trying to ask whether there are other and better ways of solving my problem.
Using channels maybe? Signal the channel (put) when thread is done and have a different thread park on the channel for output (take)
if we think about it as a system of processes (going the CSP route), let's imagine a controller process C, and a worker process W. you can implement a sort of async RPC where the controller sends messages to a worker and the worker acts on them and returns a message via a response channel, i.e.
C!cmd -> W?cmd; do-stuff; W!resp -> C?resp
When you have several workers you can just index the response and control channels
Diving a bit into the worker, it should itself contain a process that does the actual work and it will be the "interface" to it, which queries and reports if it's completed
the future
macro creates a new thread, and returns a promise that won't be ready for reading until the thread exits, and returns what the code in the future returned on deref
CSP is cool but also overkill for the literal feature asked for here
Maybe it is... I've been immersed quite deeply in it lately so I feel quite comfortable with the abstraction and semantics. Why do you think it's overkill?
core.async (the official implementation of CSP for clojure) is quite "leaky", and imposes many assumptions / restrictions on your code reading more carefully, one could implement what you describe with thread safe queues, and that would avoid many of the problems (and impose the problem of maintaining your own channel infrastructure)
the specific behavior asked for is easily built out of future and deref
then you'd use watchers and callbacks to update the shared data which will be accessed by the "supervisor" thread?
no, I'd use futures and deref:
(def state (atom {... ...}))
(def process-a (future (swap! state frob x y)))
(def process-b (future @process-a (swap! state flux z)))
the deref of proces-a ensures process-b doesn't execute the rest of its body until process-a completes, and propagates any failure to interrupt it as well
of course in real code I wouldn't put future calls in def - these would be let bindings
wrt core.async being leaky, It's something I find with all the rich abstractions (promesa, manifold, callbacks), they all leak and infect the code and you have to write in them
that's a separate problem: you need to lift other code into the abstraction to cooperate with it
by leaky, I mean that with core.async you need to: • ensure that no CPU intensive or blocking calls happen in go blocks • ensure that a given block doesn't read and write the same channel in a loop • avoid certain macros inside go blocks that the callback transform compiler can't handle etc. things that aren't just the async paradigm, but limitations of how it's currently implemented
Ah, I see what you mean. Yeah, there's a lot of discipline(?) required when working with core.async. With future/deref, how would you implement a DAG of tasks which you also want to be able to monitor? also you might want to kill, restart, etc.
well, killing of processes inside the jvm simply isn't a thing, unless done cooperatively by checking for a cancellation flag
regarding task graphs, I don't know of a good solution in clojure off hand, aside from plumatic/graph which I haven't used in anger and can't vouch for
usually people go for task distribution DSLs (which then offer distribution across multiple servers etc.) but once again, I didn't infer the need for that kind of feature from the original problem description
generally all of these things are methods of making your code less reliable and harder to debug, I wouldn't adopt any of them unless they were absolutely required for your domain
The OP is the simplified problem description, I have prior knowledge from a post a few hours prior (that may be cheating 🙃 ) I've been thinking about similar problems lately, I'm leaning towards separating execution model from data model, then it's possible to try out different solutions and break them apart to isolate the fragile/unreliable parts as much as possible
big picture, it's usually the breaking apart that makes things harder to code correctly
when you turn procedural step-by-step code into a graph of callbacks, processes, and conditions, what would be trivial surface level bugs become non-obvious
also, the call stack, which reveals the source of erroneous behavior, typically loses all meaning or utility
then the desire to describe processes as config rather than code, further detaches the erroneous state from any of the language facilities that help us ensure things are correct - a typo on a keyword doesn't throw a compile time error the way a var typo does, etc.
these tradeoffs can be worth it, but they have a huge upfront cost in code quality and maintainability that you need to repay
which is why I suggest adopting as little distribution and async as you can get away with
on top of everything else, these indirections make code slower, and the paralellism they enable doesn't always compensate
With what you said about a keyword error, that's a pertinent point, which I think is remedied a bit by the model separation. when you "compile" the data model you can overcome at least that issue
that's true, and I don't mean to imply that these techniques are never appropriate, or that we don't have good tools to reduce the problems listed
when I first learned how core.async worked I was excited about what it offered, and used it liberally in my codebase this led to very subtle and hard to debug intermittent problems, partially due to my incomplete understanding of core.async and partly due to the limits of that model definitely use it if you need async execution, and you need complex patterns of coordinating results, but it doesn't come for free
Is it okay to create a dummy namespace just to be able to use namespace qualified keywords in that namespace? Are there other ways to achieve the same effect? I know :dummy.namespace/keyword
works, but I want to avoid typing the full namespace every time.
It’s ok. You can do it with create-ns
and alias
in the place where you need it
What's the standard way of returning only the first regexp match? I realize re-find uses re-group to return results, but is it then safe to assume re-group always returns the input as first result?
> (re-find #".*[?&]some-param=([^#&]+)" "?some-param=some-val")
["?some-param=some-val" "some-val"]
If by 'first' you mean 'only one match, not multiple matches', then yes that is safe to assume.
If by 'first' you mean 'guaranteed to be the earliest match of the regex in the string', that depends on the regex and the string, perhaps. I don't have examples handy to demonstrate possible surprises there.
Yeah, I read on docs for re-groups that "
If there are
nested groups, returns a vector of the groups, the first element
being the entire match.
But for that example I had above, does that really count as a nested group?
In that regex you give, for example, does the initial ".*" part need to be there, really?
Well, I'm counting on input being possibly a full URI
And I'm looking to extract only specific query parameter values
I guess ^.* could have been better
In the regex, any parenthesized expression is a capture group, unless it begins with a special character sequence, IIRC something like "?:" just after the left paren
re-find
looks for any match of a substring that matches your regex, so using re-find
either ^. or . at the beginning are redundant, I believe.
Hm. I need to read the regexp docs again 🙂
re-matches
restricts itself to only matching the entire string.
The Clojure doc strings for those functions refer to Java APIs for the full behavior.
and those Java docs are not short 🙂
Perhaps reasonably, regexp isn't a small topic 🙂
I will think on what you wrote, thanks 🙂
If anyone has a question, feel free to ask it 😃
Hi fellow clojurians, I'm trying to read a file from the resources folder from the repl with `http://clojure.java.io/resource` . I'm using deps.edn and have added resources to the paths vector interestingly io/file works
(defn load-config [resource profile]
(-> resource
()
(read-config)
(get profile)))
the call to io/resource always returns nil
you should probably use the relative path from the resource folder
@dev-hartmann make sure the resources folder is on the classpath
@ghadi thx, that fixed it i had :path
instead of :paths
Hey all 👋 Does anyone know why 4clojure is screaming at me not to use count
? problem 22 http://www.4clojure.com/problem/22
I submitted and it says You tripped the alarm! count is bad!
for the following answer:
(fn [x] (reduce + (for [position x]
(+ 0 1))))
for must use count inside the expansion of that form
yeah, for is pretty wild
user=> (macroexpand-1 '(for [position x] (+ 0 1)))
(clojure.core/let [iter__6305__auto__ (clojure.core/fn iter__140 [s__141] (clojure.core/lazy-seq (clojure.core/loop [s__141 s__141] (clojure.core/when-let [s__141 (clojure.core/seq s__141)] (if (clojure.core/chunked-seq? s__141) (clojure.core/let [c__6303__auto__ (clojure.core/chunk-first s__141) size__6304__auto__ (clojure.core/int (clojure.core/count c__6303__auto__)) b__143 (clojure.core/chunk-buffer size__6304__auto__)] (if (clojure.core/loop [i__142 (clojure.core/int 0)] (if (clojure.core/< i__142 size__6304__auto__) (clojure.core/let [position (.nth c__6303__auto__ i__142)] (do (clojure.core/chunk-append b__143 (+ 0 1)) (recur (clojure.core/unchecked-inc i__142)))) true)) (clojure.core/chunk-cons (clojure.core/chunk b__143) (iter__140 (clojure.core/chunk-rest s__141))) (clojure.core/chunk-cons (clojure.core/chunk b__143) nil))) (clojure.core/let [position (clojure.core/first s__141)] (clojure.core/cons (+ 0 1) (iter__140 (clojure.core/rest s__141)))))))))] (iter__6305__auto__ x))
Oh wow I did not know you could do that
with a for that basic, the map
equivalent should be easy to write
(I mean I'd write it now but this is supposed to be a learning exercise after all :D)
Haha no worries…Thanks for pointing it out and macroexpand
trick
Will attempt another way
another alternative is using a function other than +
in reduce, since you are using reduce already
I've found myself knee deep in a conformed spec data structure and I want to use match to couple destructuring + binding (Erlang style, I suppose). Should I be keeping it separate (the code is fairly trivial) or should I bring in something like core.match? https://github.com/clojure/core.match
the main arguments I've heard against match:
• it leads to brittleness that prevents late binding (RH advocates using multimethods so other code can add cases as needed)
• it produces large bytecode, and since the JVM imposes a hard limit on bytecode size, combining it with eg. core.async
or for
or core.logic
can lead to compilation errors
all that said, it's a style choice, there are reasonable ways to avoid those issues, use match if it is the idiom you want for your codebase
I think in the case I won't use it as it's just for style purposes on internal code I don't care too much over
I kind of doubt it, where core.match runs into trouble is it tries to an algorithm for compiling pattern matches that was created for compiling to machine code (or some lowlevel bytecode) that assumes the presence of things like 'goto', so core.match either has to duplicate code or emulate goto (in theory using a preallocated exception on the jvm does this well, which is how I think it does it)
meander looks like it similarly tries to be an optimal compiler for pattern matches (I am not 100% sure but a quick glance leads me to suspect it is preallocating exceptions for jumps as well), so I suspect it may hit the same issues as core.match
but you can write a (dumb, with no features) pattern matching macro in less than 200 lines of code, which likely will not have those issues
We don't actually compile to exceptions for jumps in meander. I don't know for sure if the problem that's bring talked about is a good fit for meander or not. But so far I've never seen a meander match that runs into the code too large problem. And we have some fairly complex and long matches.
Those turn I to recursive function calls. You can definitely blow stack if you aren't careful.
https://github.com/noprompt/meander/commit/afc410d6d82f719cf8f39c4fedf5b1cdb761fb21 seems like a goto to me
Its a crappy hack and yes it is, however, its only used for rewrite
. The matching is compiled to letfn
and we invoke those functions instead of throw
to emulate goto
.
That isn't a go-to in the sense you are talking about as far as I can tell. It is just a hack around the fact that if something fails we throw an exception. So when we cata we need to catch the exception and return a value. We aren't jumping to different cases.
But never been a problem in practice. We are even writing the next version of meander using the current version and have had no scaling issues.
But given that this is #beginners. Not recommending everyone go off and use meander right away. You can check out my talk or blog posts if you wanted to learn more. But it is something that takes a bit to learn.
Thanks for your input. In my case just pulling a library for what I wanted was overkill but I can also see how it tightly couples the data. So the Clojure point of view is worthwhile 80% of Clojure seems to be learning the tools / libraries so I'll skim the docs 🙂
Glad you liked the talk. :) Feel free to ask any questions you have on #meander.
I am honestly struggling to convert this (reduce + (map (fn [message] (+ 0 1)) [[1 2] [3 4] [5 6]]))
to the koan style of submission 😔 where [[1 2] [3 4] [5 6]]
is meant to be any sequence
@mauricelecordier as that stands it just counts how many elements are in the sequence -- is that what you intend?
user=> (reduce + (map (fn [message] (+ 0 1)) [[1 2] [3 4] [5 6]]))
3
Needing to count the elements without using count
an earlier submission I used, used for
which uses count internally and got rejected
Yip! Its problem 22 in 4clojure
That seems to error out
Did you have (1)
instead of just 1
perhaps?
Ah yes I did
(1)
means "call 1
as if it were a function" which with throw a class cast exception (because Long
-- the type of 1
-- is not IFn
-- the expected type of a function)
(reduce + (map ,,, <some-sequence>))
=> ( #(reduce + (map ,,, %)) <some-sequence> )
is the transformation from an expression containing some sequence to an anonymous function of one argument applied to that sequence.
@mauricelecordier You might find constantly
useful here...
I see this has an example using constantly
as a way to find the size of a collection…thanks! Thats interesting
...but you don't need to map
over the elements really since you don't care about their values: consider (reduce (fn [n _] ???) 0 <some-sequence>)
for some function ???
as a possible solution shape (the _
is a convention for "I'm deliberately ignoring this argument").
n
will be 0
on the first call. What would you need ???
to be for that to count the elements?
Ah! (+ n 1)
Thanks @U04V70XH6 🙂
Or just (inc n)
which is more idiomatic.
True….I am still getting use to the language itself
Don't worry -- becoming idiomatic takes a lot of practice: clojure.core
has hundreds of functions that you slowly get used to combining.
I am about to start chapter 4 of the Brave book which deals with clojure.core
a bit more
When using the lein shell
plugin I keep getting WARNING: You have $CLASSPATH set, probably by accident
do I just have to live with this? I believe this is happening because the plugin is mostly likely executing a lein command in another session(?) and it inherits the classpath from the original lein process. But honestly have no idea
:aliases {"run-client-external" ["shell" "./scripts/run-client.sh" "external-local"]}