Fork me on GitHub
#beginners
<
2021-06-18
>
berkeleytrue01:06:03

Question around NS keywords. I was under the impression that #:foo {:bar "baz"} and {:foo {:bar "baz"} were equivalent. But if I do (assume ns is :foo) (::bar some-map) with the first one, I get "baz" but if I try it with the second one, I get nil. Are they not equivalent? Or is there another way to be able to get the value of :bar without being explicit about the NS?

phronmophobic01:06:55

the equivalent of #:foo {:bar "baz"} is {::bar "baz"} aka {:foo/bar "baz"}

👍 3
berkeleytrue01:06:19

hmm, ok. I think I see why my code isn't working. It's unfortunate

berkeleytrue01:06:24

thanks you!

👍 3
seancorfield01:06:34

@U35G2V78U namespaced keywords don’t all have to have the same qualifier in a hash map:

dev=> #:foo {:bar "baz" :quux/id 42}
{:foo/bar "baz", :quux/id 42}

seancorfield01:06:21

The #:foo prefix provides a “default” namespace to use on any keys that don’t already have one.

berkeleytrue01:06:56

oh that's interesting

berkeleytrue01:06:04

It's that access of those inner properties. I was hoping to easily get properties off a map using the name space. (in some-ns) (::foo wide-flat-map) so that I can dynamically create a map later on (merge wide-flat-map {:some-ns (generate-data)}

berkeleytrue01:06:33

Still learning so I'm mostly playing around

seancorfield01:06:13

(-> wide-flat-map :foo :bar) or (get-in wide-flat-map [:foo :bar]) if you have nesting.

seancorfield01:06:28

The names of keys and their depth in the hash map are orthogonal.

seancorfield01:06:50

But qualified keys are idiomatic and I try to encourage folks to use them.

seancorfield01:06:39

(and I’d advise against using :: unless you know what you’re doing since ::foo means something different in each ns)

seancorfield01:06:10

(ns a.b.c)

::foo ;=> :a.b.c/foo

(ns x.y)

::foo ;=> :x.y/foo

berkeleytrue01:06:17

That was kinda the point. I'm in the same ns but the generation is at runtime. I was hoping avoid doing :some-ns :foo because if I change the namespace name then I have to change all those calls. If ::foo worked then it wouldn't be a problem cause the compiler would put the right ns.

seancorfield01:06:36

How would the namespace of the code end up tied to the keys in your nested map?

berkeleytrue02:06:23

I was exporting (not sure if that is the right word) a map with the key as the namespace and a function to produce the state {(keyword (namespace ::r)) some-fn}. Pretty hacky and I'm not happy with this setup.

seancorfield02:06:15

Yeah, that sounds well dodgy to me…

seancorfield02:06:36

I guess my basic question is “why?” — what semantics does the namespace name specify in this hash map?

berkeleytrue02:06:27

It is just a way to prevent name clashes on a large flat map. I just had a thought of just using some arbitrary ns key instead of trying to get the namespace of the file {::state some-fn} and accessing would be (get-in wfm [::state :foo]) .

seancorfield03:06:17

Better to use semantically meaningful qualified names, in my opinion. :person/first-name, :address/street, etc.

seancorfield03:06:25

Make them as unique as the context requires — more global data should probably use something like reverse-domain-name + entity style qualifiers, less global data can use a simple symbol for the qualifier.

seancorfield03:06:13

If you work with JDBC via next.jdbc, you’ll find yourself with column names that are qualified by the table name (so you can do a JOIN and get distinct, meaningful names).

sova-soars-the-sora02:06:22

brain fried question... how can I set all values in a map (on all keys) to the same value?

R.A. Porter02:06:47

Here's one way...

(zipmap (keys <my map>) (repeat <my val>))

seancorfield02:06:17

If we get map-vals in Clojure 1.11 (it’s been mentioned a few times but not confirmed), you could do (map-vals (constantly <my val>) <my map>)

Ovidiu Stoica03:06:05

In learning clojure, what is the ONE thing that if you practice regularly, you will become a great clojure programmer?

phronmophobic03:06:44

I would reject the premise of the question. The difference between good and great isn't a handful of big things, it's 10,000 little things. Bonus answer from the FAQ posted not too long ago: https://gist.github.com/reborg/dc8b0c96c397a56668905e2767fd697f#how-can-i-achieve-programming-mastery

👆 3
Ovidiu Stoica03:06:38

Hmm. Let me rephrase then: If you had only 20 minutes to dedicate to clojure programming every day, what would you learn to have the highest impact?

phronmophobic03:06:43

It kinda depends on what you're hoping to get out of it. • Learn general programming concepts to apply in other languages? • Learn programming to automate workflows? • Generally learn more about how computer programs work? • Programmatic art/music? • Build a website? • For fun • something else?

Dane Filipczak04:06:36

After becoming comfortable with the core library, the highest impact set of concepts for me was becoming effective with clojure style polymorphism. defmultis, protocols, records, etc. Reading library code is often a good way to see effective usages of these features.

👍 3
didibus05:06:12

20min per day? I think going through the 4clojure exercises can be a good start, becoming good at the core functions to be able to manipulate data-structures is pretty important.

💯 3
didibus05:06:26

Once you got that down, and you have recursion all figured out, and you also see how you don't always need to use loop/recur, but can often just compose core functions instead, map/reduce/range/partition/juxt/etc. I would spend some time on different topics. Like go through all Collections and their functions and play with those at the REPL to understand them well. Then learn about sequence and laze-sequences and all of their functions, play with that at the REPL until you understand it well. Then I'd spend another few sessions on learning namespaces, Vars, what all you can do with those all their features and all that. Then I'd learn about the bootstrapping, how a Clojure program starts, where it loads its dependencies from, where it loads its module from. Then I'd try to write a full command line program, like a command line TODO app. As part of that, each time you struggle with something, I'd take a side-track to fully master that part, so say you struggle with managing the TODO lists state, I'd go learn about state management in Clojure, etc.

👍 3
didibus05:06:25

There's a lot to cover. You could also go here: https://clojuredocs.org/quickref and spend one 20min each day on each sections in here. Do one on arithmetics, then one on comparison, then one on bitwise ops, then one on cast, etc. top to bottom, order doesn't matter. And then I'd also go here: https://clojure.org/reference/reader and spend a 20min on each of those sections too (you might need more than 20min for some of these). Do one on the reader, then on repl and main, then on evaluation, etc. Until you grasp each one. After all that, you should start to feel comfortable doing full on projects, and that'd be my next move from this point on.

thanks2 3
Alexander Moskvichev08:06:50

I wrote a code that is suddenly extremely slow. (:require [clojure.data.xml :as data-xml]             `[clojure.xml :as xml]`             `[http://clojure.java.io :as io]`             `[clojure.zip :as zip]`             `[clojure.pprint :refer [pprint]]`             `[fipp.edn :refer [pprint] :rename {pprint fipp}])` (defn iter-zip [zipper]     `(->> zipper`          `(iterate zip/next)`          `(take-while (complement zip/end?))))` (with-open [r ( "data/data_202106160959.xml")]     `(with-open [w (http://clojure.java.io/writer "data/goods.edn")]`       `(-> r`           `xml/parse ;; about 15sec`           `zip/xml-zip ;; another 15sec`           `iter-zip ;; plus 60 sec`           `;(filter loc-category?)`           `(fipp {:writer w}))))` It takes minutes for a 15Mb input file. What I'm doing wrong?

borkdude11:06:04

@alexander.moskvichev what is the zipper doing?

borkdude11:06:45

I think the iter-zip function is creating lots and lots of objects

Stuart11:06:15

Even so, 15 sec to parse 15 mb xml seems a lot, no?

Alexander Moskvichev13:06:02

@borkdude This is my first attempt to use clojure.zip Zipper suppose to be lazy, but I can use it wrong. In between reading and writing will be the filtering and transforming stage, I planned to use clojure.zip for navigating through XML.

borkdude13:06:09

@alexander.moskvichev It seems you are printing all intermediate zipper objects

borkdude13:06:28

you should at least call zip/node on it, to get back xml

borkdude13:06:39

I don't know what you're trying to do really

borkdude13:06:48

Start with a small example

Alexander Moskvichev13:06:37

@qmstuart I think so, but 15 sec is ok for now.. clojure.xml/parse not lazy, in that file roughly 20k items each with 10-20 properties. Maybe it's a normal time for one thread.

Apple13:06:22

I used clojure.data.xml/parse and it is lazy.

Alexander Moskvichev14:06:21

@UP82LQR9N That right. But I use clojure.xml, not clojure.data.xml. Sorry for the mess in (:require...)

Apple14:06:44

Perhaps for large xml file clojure.data.xml is better. It mentions this as one of its features "lazy - should allow parsing and emitting of large XML documents"

Alexander Moskvichev14:06:12

@UP82LQR9N.. Shure, I even 'require it already. 🙂 But I think my main problem somewhere else

Apple15:06:34

I have a 90MB xml file 2.2 mil lines I replace fipp with just count and clojure.xml with clojure.data.xml Test briefly with time and it's inside a virtualbox vm. It takes 1.7sec 2.0sec 17.3sec up to parse xml-zip iter-zip respectively and the final count is 4.6mil

emilaasa20:06:20

The zipper object is the whole tree (xml doc) and your position so printing it might be expensive.

Alexander Moskvichev07:06:10

@U6T7M9DBR @borkdude Thanks. I was calling pprint for the whole file for every zipper iteration. Of course, it was slow, lol. @UP82LQR9N Switched to data-xml. I appreciate that you spent time testing.

MatthewLisp14:06:48

Hello fellow clojurists Is there an equivalent of Leiningen's :java-source-paths for clj deps.edn ?

noisesmith14:06:45

deps.edn on its own does no java compilation for you

noisesmith14:06:59

there are various tools for deps that do this, they have their own config

alexmiller15:06:29

coming soon ...

😎 12
excited 18
👏 9
walterl23:06:43

Speaking of RDD: how do you guys eval a form in the REPL that contains symbols defined outside of that form? E.g. I'd like to eval the (super-hard-calc x) form in the body of the let in this contrived example:

(def super-hard-calc inc)

(let [x 1]
  (super-hard-calc x))
Currently I copy the form into a Rich comment, and then replace or let values for all required symbols. But it means that, if I make any changes, I've now added a syncing problem. 😛 I'm using vim + Conjure, but solutions in any env are welcome.

didibus00:06:28

Like assuming there was also other forms inside the let and you only wanted to eval super-hard-calc ?

seancorfield00:06:57

Instead of copying the form into an RCF and wrapping it in let etc, I tend to just add a def inside the RCF to (temporarily) define a global, then I can eval the original form in-situ.

3
seancorfield00:06:03

I have a hotkey for turning parts of let bindings into global def’s too. Consider:

(let [x (super-hard-calc y)
      z (another-hard-calc x y)]
  (do-stuff-to z))
I’ll manually create a def for y in an RCF and then I can eval that first expression — but I can also now highlight x (super-hard-calc y), hit a hotkey, and x is now globally defined with that value. Then I can repeat for z (another-hard-calc x y) and then I can eval (do-stuff-to x) in-situ.

3
❤️ 3
💯 3
walterl00:06:03

Thanks @U04V70XH6. Sounds like an interesting feature to try and add (if I'm not overlooking existing functionality)!

walterl00:06:29

@U0K064KQV Yes, but not necessarily. The question is really about evaluating a single form that include symbols will need definition. E.g. I want to eval only (super-hard-calc x), but that will fail, because x has no value unless I eval the surrounding let too.

walterl00:06:32

vim-fireplace actually has an almost-solution: https://github.com/tpope/vim-fireplace/blob/master/doc/fireplace.txt#L215-L225 > "Bring up a |command-line-window| with innermost form at the cursor prepopulated." Implying that the form can be edited (symbols replaced with values) before actually being eval'ed. But I've found editing in that window quite difficult.

didibus00:06:07

Generally I just edit x to be whatever value I want and then I eval that

didibus00:06:35

(let [x (foo (bar (inc (something))))]
  (super-hard-calc 10))
And I put my cursor after (super-hard-calc 10) and I do eval-last-sexp

didibus00:06:56

When I'm done I just put x back

didibus00:06:32

And sometimes if I don't want to forget what it was, Then I use the discard reader:

(super-hard-calc 10 #_x)

didibus00:06:14

I do the same thing in the let if I want to see some things flow through:

(let [a 10 #_(get-foo)
      b (inc a)
      c (super-hard-calc (+ a b))]
  c)

walterl00:06:39

Hmmm, yeah. Maybe I should KISS is better 👍

didibus00:06:33

Ya, for anything more complicated then that, I use the Cider debugger: https://docs.cider.mx/cider/debugging/debugger.html

emilaasa08:06:09

I do what (I gather) Sean does; (def x 1) anywhere in the file, for me in a comment block, will make x available anywhere, as long as you don't overwrite it. So in this case you'd avoid evaluating the surrounding let and just eval the calculation.

❤️ 3
emilaasa08:06:25

(def super-hard-calc inc)

(let [x 1]
  (super-hard-calc x)) ; 11

(comment
  (def x 10) ; #'conjure.internal/x
  ,)