Ya, I got the impression print-dup is very much an internal detail, I don't know for what exactly. But for the user, you would want to extend print-method instead, see here: https://groups.google.com/g/clojure/c/R-9Pwk3HcFk
This is true for prn and pr-str as well
what's the proper way to parse HTML? there's clj-xpath, but that expects valid XML. should I have a look at hickory? or is there some other library I didn't find yet?
https://jsoup.org/ probably
I used Hickory in the past and it worked well
Hickory https://github.com/davidsantiago/hickory/blob/ea248a6387f007dc4c4e8fcbbafbb1b9cbc19c78/project.clj#L10 to be built from jsoup
I personally like jsoup a lot
I thought you were supposed to use regex?
@alexmiller I think using clojure.spec.alpha is the correct approach
it's been some years, but a huge gotcha for the jsoup bindings I found: it returns a lazy-seq of document tags and data, and it creates a gc root for the underlying jsoup object in a helper thread it only allows the jsoup object to be freed if you consume the entire document the issue I ran into is that I wanted to lazily scan each document for a specific tag containing matching data (it's a lazy-seq, this is what they are for right?) and the book-keeping the library did meant I had to choose between reading an entire document I don't need or leaving the jsoup object hanging as unrecoverable garbage in the vm
yet another example of "never mix laziness and state"
jsoup bindings? just use jsoup directly
💯
(it was years ago but my tech lead at the time was highly averse to adding any java interop to our codebase)
Hey guys. Trying to use clojure.spec to write a simple string calculator, i.e. "2+2" => 4. It works, but it seems really verbose! I also don't like how I used regex-replace before conforming to spec (I'd rather use 100% spec). Any ideas how I could clean it up?
(ns demo.calculator
(:require
[clojure.spec.alpha :as s]
[clojure.string :as string]))
(s/def ::digit (set "0123456789.n"))
(s/def ::operator (s/cat :kw #{:operator} :val char?))
(s/def ::expression (s/* (s/alt :number number?
:operator #{\/ \* \+ \-}
:parenthetical ::parenthetical)))
(s/def ::parenthetical
(s/cat :open #{\(}
:body (s/* (s/alt :number number?
:operator #{\/ \* \+ \-}
:parenthetical ::parenthetical))
:close #{\)}))
(def order-of-operations
[[:operator \*]
[:operator \/]
[:operator \+]
[:operator \-]])
(defn some-index [coll ops]
(if (empty? ops) nil
(let [i (.indexOf coll (first ops))]
(if (= i -1)
(some-index coll (rest ops))
i))))
(defn evaluate [expression]
(let [index (some-index expression order-of-operations)
or-paren #(let [e (Exception. (str "invalid arithmetic at " %))]
(case (first %)
:parenthetical (-> % second :body evaluate)
:number (second %)
(throw e)
))]
(if-not index (or-paren (first expression))
(let [[left-hand operator right-hand :as operation]
(take 3 (drop (dec index) expression))
before (take (dec index) expression)
after (drop (+ 2 index) expression)
result [:number ((resolve (symbol (str (second operator))))
(or-paren left-hand)
(or-paren right-hand))]]
(evaluate
(concat before
[result]
after))))))
(defn parse-math [arithmetic]
(->>
(string/replace
(string/replace arithmetic #" " "") #"(^|[^0-9])-([0-9]+)" "$1n$2")
seq
(partition-by #(s/valid? ::digit %))
(map #(if (s/valid? ::digit (last %))
(Integer/parseInt (string/replace (apply str %) #"n" "-"))
%))
flatten
(s/conform ::expression)
evaluate))
(= 1/2 (parse-math "1/2"))
(= 42 (parse-math "(((((((((((42)))))))))))"))
(try (parse-math "2+*2")
(catch Exception e true))
(= -4 (parse-math "-1 * (2 * 6 / 3)"))
What is the time and space complexity of the core count function?
On a vector or map, O(1)
On a lazy seq, you have to realize the seq to count it
why the hell does the racket implentation of lisp have a O(n) time for the built-in length function?
don't they use linked lists?
clojure has counted?
to check if a coll implements count in constant time
How are vectors and maps implemented differently in Clojure to give O(1) time for count
as opposed to O(n) time for length
in racket? And is the space complexity for count
O(1) too?
count
is O(1) in Racket on vectors and can be O(1) or O(n) on hash tables. The big difference is that cons lists and assoc lists (cons lists of key value pairs) are much more idiomatic in Racket, and the vector implementation is much less generally useful than in Clojure.
Racket doesn’t provide a persistent vector with a constant time vector-set!
function; you can only update mutable vectors. Clojure gets around that by using a comparatively recent data structure for maps and vectors, the Hash Array Mapped Trie which allows amortized constant updates and access.
I don't think anything about Hash Array Mapped Trie is needed for the O(1) count though, Clojure just literally counts elements as calls to assoc are made.
@didibus can you point me to the loc’s where this mutation on count’s value happens?
Ya, if you look here: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentHashMap.java#L139
For maps, each assoc implementation returns a new map with the count parameter set to count + 1
and here for vector on cons is the same: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentVector.java#L228
There's lots to be written about how to implement data structures with different time/space complexities for various operations. I think why may be more interesting than how. From https://download.clojure.org/papers/clojure-hopl-iv-final.pdf > 3.4 Clojure’s Data Structures > > While linked lists are available and can be implemented in most programming languages, and have a certain elegance and functional heritage, their use is dominated in professional practice by the use of vectors/arrays and hashtables/associative maps. Of course, lists are sequential like arrays and one can access the nth element, and e.g., Common Lisp has a notion of association lists and a function ‘assoc’ for creating mappings and thus treating a list of pairs like a map. But it highlights an important aspect of programming and difference from mathematics in that merely getting the right answer is not good enough. Not all isomorphisms are viable alternatives; the leverage provided by indexing support matters. I felt Clojure would be a non-starter for practitioners without credible substitutes for O(1) arrays and maps.
Answering how clojure implements its data structures is a little complicated since maps and vectors are primarily defined by their interface rather than their implementation. In fact, the implementation can "change" as these collections shrink and grow even though the interface stays the same.
user=> (type (zipmap (range 8) (range 8)))
clojure.lang.PersistentArrayMap
user=> (type (zipmap (range 9) (range 9)))
clojure.lang.PersistentHashMap
The simple "how" is just that Clojure map and vector keep track of the count as elements are added/removed, so when you ask for the count, it's already been computed, you don't have to go through and count things.
is there a way to remove a var from the current namespace? I did a (apply require clojure.main/repl-requires)
not realizing there is a function in this ns called source
. Now re-evaluating the file errors saying that source already refers to: #'clojure.repl/source in namespace
is it not (ns-unmap ns ’do-something)
that's exactly it
i had forgotten it was unmap. i was looking at apropos results for remove and var
thanks dane!
The code my “clean ns” hot key is bound to: https://github.com/seancorfield/vscode-clover-setup/blob/develop/config.cljs#L68-L78
Removes aliases, interned names (public + private), refers except core.
@seancorfield so cool, borrowing this : )
neat. thanks sean
It’s nice because it cleans out a namespace without actually destroying the ns itself, so loading the file “does the right thing” and any other nses that hold references to this ns — via :require
— don’t get broken. I don’t have to use it very much, but it’s “just enough cleanup” to avoid any of those reload/refresh things…
oh i'm jealous reading this config in cljs instead of elisp 🙂
I need to serialize HTTP (Ring) requests for a debugging tool I'm writing in ClojureScript. Each request is enriched with extra data, such as plugins (functions) that have been loaded, and random stuff like Reitit Match
records. These are things that the ClojureScript environment isn't going to know about so I need a generic, legible way to represent them to the user. What's a good strategy for doing this? I found this article about https://tech.redplanetlabs.com/2020/01/06/serializing-and-deserializing-clojure-fns-with-nippy/, but I think that's overkill for what I need (and Nippy doesn't work in CLJS).
VS Code/Clover or Atom/Chlorine — exact same config (and hot keys) work on both.
If nippy is overkill, then maybe serializing your data as edn is appropriate?
If you don't need to store data and are just transmitting data between applications, then transit might be a good fit, https://github.com/cognitect/transit-clj
> maybe serializing your data as edn is appropriate?
That's the idea, but that results in fns that the EDN reader doesn't know how to deserialize client-side. Here's the current process, for context:
• Enrich each request on the server as it comes in
• Call (prn-str req)
and send it over a websocket to the debugger
• On the debugger client side, do (edn/read-string req)
I think the issue here is with prn-str
being too simplistic
If you're serializing to transmit between applications and aren't storing the data, then check out transit
I'm not aware of any serializer that can generically serialize functions. You'll probably have to preprocess the data or ignore that part of the data.
I need to make an appeal to the collective memory here: somewhere on youtube, there is a video of a presentation about (as far as i remember and it's killing me) building a DSL out of ASTs constructed from a sample data structure about planets(?). My best guess for a time frame is three or four years ago. Does that ring any bells?
There are also some caveats when using pr-str
like *print-length*
. Let me see if I can remember a good reference that covers them.
Yeah, I guess what I'm asking about is the pre-processing part. I need to transform functions and other "unkown" entities like third-party records into simple symbols or strings, something that won't result in tagged values like #object
that EDN/CLJS would need to load extensions for.
This reference seems pretty good: https://github.com/jafingerhut/jafingerhut.github.com/blob/master/clojure-info/using-edn-safely.md
Transit is great, I will probably switch to it eventually to avoid the *print-length*
trap etc. But AFAICT it'll still come up against the limitation of not knowing how to deserialize random library object client-side. It's okay to lose some fidelity in those cases and just render strings instead...so maybe what I'm looking at is extending Datafiable
for fns etc. via metadata? Just a little hazy on how that would work. 🙂
another option is to use clojure.walk/prewalk
or similar and remove any types you don't recognize
Right, makes sense. Talking through this has been helpful, thanks!
That’s Tim bald ridge making a logic engine at the Denver clojure meetup
On GitHub he’s halgari. Look in his repos for “logic” and you’ll find it
Unfortunately, I can't find anything recent that does a good job of explaining the "right" way to write edn.
Has anyone played around with gitpod and a clojure environment?
Aha. It was searchproof because it's about zippers!
I'm not too worried about it as I'll likely switch to Transit and translate anything unrecognized into stuff it can handle out of the box.
Thanks. This has been tormenting me for weeks.