Fork me on GitHub
#beginners
<
2019-10-11
>
noisesmith00:10:25

(I guess there's a small chance that -> form is incorrect because ($ 1) or ($ :type) might not be a map lookup, but I find it unlikely it would be anything else in context)

seancorfield00:10:20

Also (since this is one of my "pet peeves"), as-> is designed to be used inside -> for cases where the value you're passing in is neither the first nor last argument (for the latter case you can nest ->> inside ->). So it is more normal to see:

(-> exp
    (f 2 3)
    (as-> x (g 1 x 3))
    (h 2 3)
    (->> (map foo)))

darrell00:10:49

@noisesmith Yes, that was an example from the Clojure docs.

hiredman00:10:53

http://Clojuredocs.org are not official clojure docs, so you need to be dubious of what you find there

andy.fingerhut00:10:53

A lot of what is there is good, but the quality does vary

clojurians-slack10001:10:06

In Python one can strip characters from the end of a string with 'foo/'.rstrip('/'). Is (clojure.string/replace "foo/" #"/$" "") the Right Way to do that in Clojure?

seancorfield01:10:39

If you only want to strip one / off, that would work. #"/+$" would match one or more / at the end of the string.

seancorfield01:10:24

I don't know whether I'd say regex is the Right Way but it's either that or checking (str/ends-with? s "/") and then returning (subs s 0 (dec (count s))) which may be clearer or not 🙂 but might well be slower.

seancorfield01:10:56

Looks like the if / ends-with? / subs approach is faster if that matters @clojurians-slack100?

seancorfield01:10:12

user=> (time (dotimes [n 100000] (str/replace foo #"/$" "")))
"Elapsed time: 91.607793 msecs"
nil
user=> (time (dotimes [n 100000] (if (str/ends-with? foo "/") (subs foo 0 (dec (count foo))) foo)))
"Elapsed time: 33.313469 msecs"
nil

seancorfield01:10:44

(I ran it several times and that's about the average -- but all the usual caveats about micro-benchmarks apply 🙂 )

clojurians-slack10001:10:34

Very cool, thanks @seancorfield!

clojurians-slack10001:10:23

I tend towards the more readable one for single cases, so I guess I'll stick with str/replace 🙂

mvar40k07:10:19

@adamfranzen check if you're in the same namespace as the one you defined foo in

k.i.o07:10:13

which channel will be a proper one to ask java memory model question?

leonoel08:10:23

@k.i.o here

k.i.o08:10:52

Just need a confirmation that JMM is weakly ordered and such order of reads is allowed for a non-synchronized code.

leonoel08:10:59

if t1 runs test concurrently with t2 writing v, t1 can get the old value

leonoel08:10:14

but you can easily fix this using the stack, e.g return (r.v = v);

k.i.o08:10:31

@leonoel the question is can test's return value be older than r.v

k.i.o08:10:57

if v was changed by other thread, before test run

k.i.o08:10:40

t1 v = 1
t2 return v:0
t2 r.v = v:1

abhi18av10:10:18

Hi Clojurians, I have a simple problem for which I’m trying to find an idiomatic solution - basically transforming a map of maps into a vector of vectors

(fun {{:a 1} {:b 1}} )
;;=> [[:a 1] [:b 1]]
CORRECTION A list of maps, instead of a map of maps
(fun ({:a 1} {:b 1}))

abhi18av10:10:18

Hi Clojurians, I have a simple problem for which I’m trying to find an idiomatic solution - basically transforming a map of maps into a vector of vectors

(fun {{:a 1} {:b 1}} )
;;=> [[:a 1] [:b 1]]
CORRECTION A list of maps, instead of a map of maps
(fun ({:a 1} {:b 1}))

regen10:10:26

Here's one way to do it (reduce-kv (fn [acc k v] (reduce into acc [(vec k) (vec v)])) [] {{:a 1} {:b 1}})

regen10:10:36

This one's even nicer (reduce (fn [v e] (into v (mapcat vec e))) [] {{:a 1} {:b 1}})

regen10:10:14

This is too much fun

user=> (mapcat #(mapcat vec %) m)
([:a 1] [:b 1])
user=> (into [] (mapcat (partial mapcat vec)) m)
[[:a 1] [:b 1]]

abhi18av10:10:45

Hehe, nice @ :+1:

abhi18av11:10:02

So, when I added this to the actual pipeline it also flattens the structure 🤔

abhi18av11:10:04

I think this because the sort-by actually returns a list

jaihindhreddy11:10:14

{:a 1} {:b 1}} was the example you gave. Is this a valid input too: {{:a 1 :b 2} {:b 1 :c 3 :d 4} {:e 1} {:f 2}}. If yes, what's the expected output?

abhi18av11:10:41

yeah, my bad about the example - I kind assumed that sort-by would return a map. Here’s how I built upon the wonderful solutions by @ But wrapping this into a hash-map makes me loose the sorting as well.

abhi18av11:10:09

@, na, it’s basically a map version of idents which I’m expecting

mfikes11:10:25

I’m not following the above. If the problem definition is still to take a list of maps to a vector of vectors, you might be able to rely on the fact that a map entry is a vector and do something simpler like:

(mapv first '({:a 1} {:b 1}))

regen11:10:29

If it's a list of maps instead you can use (into [] (mapcat vec) '({:a 1} {:b 1}))

regen11:10:57

But I think @ has found the most idiomatic solution [=

mfikes11:10:06

Its not clear to me if the input maps are singleton maps.

abhi18av12:10:21

Ah, yes @ I ended up building upon what @ mentioned earlier and mapping an anonymous function over the list - but this simplied it to just first - thanks :+1:

noisesmith17:10:40

`(apply concat '({:a 1} {:b 1}))` I think I missed important context in this thread - but this works for the original input / output and = as a test

chase-lambert17:10:54

When reading clojure code I don't seem to see any explicit error handling like try/catch or whatever (I'm really naive on this stuff for any language, not just clojure). I've explored using :pre and :post conditions a little bit.

chase-lambert17:10:06

But I feel like this quote from Alex Miller on a current r/clojure discussion might hint at the "idiomatic" philosophy: "In general, Clojure is aggressively polymorphic (over nil, etc) and with a bit of familiarity with common patterns, it's feasible to write Clojure code that doesn't throw errors at all. "

chase-lambert17:10:15

Can someone go in more depth of what this might mean? What would those patterns look like? What is your approach to error handling and where would you guide a junior dev about it? (that one might be too broad, apologies)

noisesmith17:10:19

looking up a key that isn't found doesn't throw, it just returns nil (or the default you specified, if any), and most functions do something useful if you give them nil instead of an expected data type, the overall idiomatic style is less concerned with detecting and rejecting error conditions and more with maximally general data operations that do a reasonable default with unexpected data if possible

chase-lambert17:10:38

ok. that makes sense. Could you give me an example of a reasonable default? Like just "ask for input" again or something, or just carry on with the program in some way?

noisesmith17:10:58

I only expect to see try around host interop (where classes from java land are more eager to throw) and I only expect throw to happen at the edge of the system, when detecting invalid reuqests from other systems or invalid states in the interaction with some other server

chase-lambert17:10:36

Awesome. I snipped Alex Miller's quote where he seems to be in agreement with you on that.

chase-lambert17:10:10

But what are examples of what you do when you get nil values

noisesmith17:10:47

usually idiomatic clojure is built on the clojure built in data functions, which treat nil as an empty list where appropriate, and return nil when doing lookup on nil or an a key or index that isn't found

noisesmith17:10:58

so you get those defaults for free from the stdlib

chase-lambert17:10:47

and so your function logic just accounts for if you get those "free" nils instead of the value you probably wanted?

noisesmith17:10:55

I let the built ins handle the nils up to the edge (when possible), and the edge (that is, the top level process code or the entry point where a request comes in) can give a reasonable error to the client / programmer

noisesmith17:10:05

right, precisely

chase-lambert17:10:45

that reasonable error part. Is that like a

if (nil? something) 
"error: you need to do this"
(else-do-stuff something)

noisesmith17:10:30

yeah - it's probably data returned to a client so they can retry / fix their code, plus a log to aid debugging

noisesmith17:10:35

no throw even needed

noisesmith17:10:07

better yet, instead of just using a conditional on nil, you can use a spec on the input or result and use that to construct a meaningful messsage

noisesmith17:10:37

though throwing an ex-info hash map where the data can be inspected on catch is sometimes simpler - depends on the shape of the application code

chase-lambert17:10:06

ahhh. cool. ok. I haven't started wrapping my head much around spec.

jumar18:10:30

It's a good practice to have a top-level error handler - like in a webapp; when everything else fails you catch the error, log it and return a (somewhat) friendly error message. Also, if you create new threads it's wors to install UncaughtExceptionHandler (see here https://stuartsierra.com/2015/05/27/clojure-uncaught-exceptions) For things like future that doesn't work though so you may want to wrap body in try-catch or use logged-future (https://github.com/ptaoussanis/timbre#logging).

noisesmith18:10:28

the reason it doesn't work for future is that there's an intentional design decision not to throw until the value of the future is accessed (by dereffing) - this is a behavior of the underlying class that comes with the jvm, instead of a try/catch or logging-future you can ensure that a finished future is eventually dereffed if you care about failures

chase-lambert20:10:46

Looking for some regex help (I think). I want to split a string like "2AB3cD4e" into ["2A" "B" "3c" "D" "4e"] so with some repl experimentation I have (re-seq #"\d[a-zA-Z]" text) but that leaves out all single letters like "B" or "D". Various other attempts give me different things too but I can't quite get what I want. Any help? Would you approach this altogether differently?

alexmiller20:10:36

(re-seq #"\d?[a-zA-Z]" text) ?

chase-lambert20:10:11

?!!! Thanks. Man, regexes kick my butt sometimes.

chase-lambert20:10:25

So regex is pretty universal across languages, right? If I find a general regex tutorial on the interwebs it should be directly applicable or should I learn a specific java way?

alexmiller20:10:09

the basics are pretty universal, the details or advanced usage usually depend on which flavor you're using

alexmiller20:10:22

but Clojure just re-uses Java's so that is the one to look at

ghadi20:10:37

top part of that page is the reference for the Java api

chase-lambert20:10:24

thank you! ? seems to be a "once or not at all" pattern.

andy.fingerhut20:10:56

I am not an authority on this, but having seen quite a few regex languages over the years, the meanings of these characters is good to learn, and common across all or most of them: [ ] + * ? | . After that things like \s \S \d I use most commonly. After that I don't trust that I will be doing enough with that one regex library that I will remember what is common across them and what is not.

andy.fingerhut20:10:11

This web site has way more detail than I ever want to learn on the subject, but is probably a good resource if you want to (a) learn the basics, (b) find some cool tools that show interactive color highlighting of a regex against sample text, to more quickly debug your regexes, and (c) dig into the gory details of the differences between different libraries, if you want to. https://www.regular-expressions.info/

chase-lambert20:10:56

awesome. I'll bookmark it. I also was recently suggested this one: https://regexone.com/ and it looks good too.

andy.fingerhut20:10:58

As I may have already strongly hinted at, I would recommend learning the basics of regex's first, and if you start getting into things where you are using other features, e.g. lookahead, lookbehind, etc., consider instead using a more powerful parser, e.g. for Clojure something like instaparse

sova20:10:30

Howdy y'all, anybody got a problem? 😄

andy.fingerhut20:10:56

How hard of a problem are you looking for? 🙂

sova20:10:42

hmm, what's your hardest one?

andy.fingerhut21:10:23

Prove that P=NP, or that P!=NP 🙂

sova21:10:02

i meant a useful problem

sova21:10:28

But I'll try to show that P = NP.

andy.fingerhut21:10:51

That would be cool.

joshlemer21:10:01

I wonder why they decided to make clojure map destructuring work like “value key, value key” instead of the arguably more intuitive “key value key value”:

user=> (let [{a :a b :b c :c} {:a 1 :b 2 :c 3}] [a b c])
[1 2 3]
I intuitively reached for
user=> (let [{:a a :b b :c c} {:a 1 :b 2 :c 3}] [a b c])
[1 2 3]
Edit, sorry the last one is:
user=> (let [{:a a :b b :c c} {:a 1 :b 2 :c 3}] [a b c])
Syntax error macroexpanding clojure.core/let at (REPL:1:1).

seancorfield21:10:41

Because bindings typically have symbol then expression.

seancorfield21:10:47

(I think that's why)

seancorfield21:10:14

If all the keys match the symbols you want, you can use (let [{:keys [a b c]} ,,,] ,,,)

joshlemer21:10:24

at first I thought it was so that you could put any function on the right hand side, that keywords were just a special case, but:

user=> (let [{a :a b :b c :c size count} {:a 1 :b 2 :c 3}] [a b c size])
[1 2 3 nil]
I had suspected it would return [1 2 3 3] oh well

seancorfield21:10:06

user=> (let [{a 'foo b :bar c "quux"} {'foo 42 :bar 13 "quux" 99}] [a b c])
[42 13 99]
user=> 
^ @joshlemer

seancorfield21:10:46

Most maps have keys that are keywords -- but you can have symbols and strings too.

sova21:10:01

(Disclaimer: i'm a newb so, school me) P = NP. or why the equals sign doesn't make sense in asking about the equality of transformational potential. We have a question of results from transformations and their equality. Computer Science is fundamentally about naming and counting. When we count the number of motions required to achieve a transformation, we are reifying certain subsequences of the transformation. Due to physical necessity, counting requires a drawer or a register in which to store a value. However, in philosophy, the question of transformation is much cleaner: Can the water that become the ice also become steam? However, it seems obvious that a robot could tie shoes so tightly and so complexly (in a complex fashion) that it would be nigh impossible to untie. However, the exact transformation that got us there is also the exact reverse transformation that can get us out. In one-way mathematical transforms over fields, the question of information entropy is alive and well, although deteriorating like swiss cheese. However, no matter how complex the tying of the shoelace, it is still a shoelace. In this way, no matter what we do to transform the data, we end up with data. Can we reintegrate data when meaning has been obscured? It's not clear if all complex transformations have more than one solution mapping, but because they all have at least one solution mapping (it has never been shown that a problem has zero solution mappings) then P must equal NP. Only if someone were able to demonstrate that there is absolutely no way to map between the two domains for a given problem could we say they are distinct sets, but to my knowledge this is yet to occur, and unlikely to be the case, even if so at first glance.

andy.fingerhut21:10:07

P is a set of computation problems, not a set of transformations.

andy.fingerhut21:10:37

Maybe best to discuss in #off-topic ?

sova21:10:07

The main question is can you transform each problem that lives in NP back into a P problem, my claim is yes

sova21:10:34

Well, my claim is that there is at least one way to do it for every single problem in NP. Until someone shows an NP problem that has 0 mappings to P i believe this hypothesis shall hold true, and if it holds true long enough it may be regarded as fact. P seems to be more finite than NP but I think they are both infinite fields and therefore able to accommodate one another. Anyway my main method there is proof by contradiction: if there is not zero answers then there must be >0 answers.

sova21:10:14

contradiction or induction is all we have as far as proving goes

lucio21:10:58

There’s a 1million prize for the person who shows that .. give it a go :)

seancorfield21:10:17

This definitely belongs in #off-topic not in #beginners

seancorfield21:10:29

And just like {:keys [,,,]} you can have {:strs [,,,]} or {:syms [,,,]} @joshlemer

alexmiller21:10:10

destructuring always tries to put the symbol being bound on the "left" side of the let

noisesmith21:10:55

@joshlemer consider {{:keys [a b]} :top {c :C_IS_POORLY_NAMED} :top}

noisesmith21:10:12

it's useful and legal to use the same value twice in hash-map values

noisesmith21:10:22

it isn't legal to use them twice as keys - the reader rejects it

alexmiller21:10:59

I would say the mnemonic of "key value, ..." leads you in the wrong direction - it should be "binding value, binding value, ..."

alexmiller21:10:49

@noisesmith seems like it would be more useful to just combine those though

noisesmith21:10:25

ahh, they can be combined, true

noisesmith21:10:38

I vaguely recall a valid and interesting usage of the same key twice in a destructure, used in different ways, but the example is clearly escaping me (or I missed something the first time around as you point out)

joshlemer21:10:31

ok I see so it’s that they chose to be consistent with other places bindings happen, rather than try to be consistent with how maps look. Nice!

alexmiller21:10:38

sequential destructuring leads you into thinking about it as a pattern or template, but that's not a correct model

alexmiller21:10:50

it's about describing structural binding

joshlemer21:10:37

Topic change: I find transducers really cool in how they decouple the transformation from the underlying “sources”/“sinks”. But do they handle only the single-input single-output case such as collection operations, or can they encode multi-input/multi-output transformations as well, like fan-in/fan-out/routing?

alexmiller21:10:50

generally, no

alexmiller21:10:34

in the very special case of map in sequence context, the map transducer can take multiple input streams

alexmiller21:10:06

the arities in this case fight with a lot of other uses so it's not done anywhere else

alexmiller21:10:59

the "pull" model of how transducers are implemented means that multi-out or fan-out are not a good match (unless you are packing multiple outputs into a single output collection)

alexmiller21:10:38

that said, core.async does have all those kinds of things and allow transducers on individual pipes (by embedding them in channels, or by using multi-threaded transducible pipelines)

joshlemer21:10:13

oh okay thanks I’ll have an other look

mdecagny22:10:14

Hi everybody, I am trying to get an existing project setup in InteliJ/Cursive. I created a Leiningen configuration, I am getting " Run Configuration Error: The Clojure jar is not attached to this module" . Does anybody have direction to help me out ? Any help is greatly appreciated.

andy.fingerhut22:10:47

I do not know the answer, but wanted to mention that if no one else here has the answer, there is a #cursive channel that might have more knowledgeable folks participating.

mdecagny22:10:22

thank you !