This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-08-20
Channels
- # admin-announcements (1)
- # announcements (1)
- # beginners (115)
- # calva (31)
- # cider (25)
- # clj-kondo (47)
- # cljdoc (23)
- # cljs-dev (5)
- # clojars (1)
- # clojure (60)
- # clojure-australia (1)
- # clojure-europe (23)
- # clojure-nl (3)
- # clojure-norway (2)
- # clojure-spec (3)
- # clojure-uk (18)
- # clojurescript (49)
- # community-development (1)
- # cursive (4)
- # datahike (2)
- # datascript (3)
- # datomic (36)
- # deps-new (2)
- # emacs (2)
- # events (9)
- # fulcro (6)
- # graphql (2)
- # gratitude (13)
- # holy-lambda (1)
- # introduce-yourself (10)
- # macro (2)
- # malli (5)
- # meander (9)
- # news-and-articles (5)
- # nextjournal (1)
- # off-topic (32)
- # pathom (17)
- # pedestal (13)
- # polylith (4)
- # protojure (4)
- # reagent (4)
- # sci (27)
- # shadow-cljs (2)
- # show-and-tell (2)
- # specter (3)
- # tools-deps (7)
- # xtdb (16)
Hey team, I am playing with writing an implementation for https://sep.yimg.com/ty/cdn/paulgraham/bellanguage.txt?t=1595850613& The first challenge, is parsing a string of bel code.
(bel-parse "(foo bar)")) -> '(foo bar)
Now, this is tantalizingly close to clojure’s read-string
. However, I can’t just pass code to read-string, for two reasons:
1. Bel has a more loose description for characters. Effectively anything with a \
prepended counts as a character
(bel-parse "\abc") -> \abc (this is a "character" in Bel)
2. Bel supports writing macros, so I’ll need a way to tell clojure’s reader, to not do anything with the normal macro writing helpers
(bel-parse "`(foo ~x)") -> # TODO: need some way to get the information about "`" and "~", without clojure's reader consuming it
Because of these two issues, I guess I would have to write my own parser, and I can’t just rely on read-string (unless read-string is much more configurable than I thought). If this is the case, is there a good parser library you’d recommend in clojure?Instaparse
We use Instaparse at work @stopachka to turn an English-like search rule language into a data structure that we then turn in JSON to send to Elastic Search.
oo — that sounds awesome, thanks @seancorfield! Will play with it
I have problem getting the intermediate values with the combination of (map & recursion).
I would like to add span
(hiccup) according to the nested level of a vector.
you could see the level result printed in the repl is correct, but how can I get the evaluation of for loop as well.
If I move for loop
behind the recursion, because do
will evaluate to the last form and this will break the receiving value of the map function of the previous level of recursion. Should I add an external source (like atom) to save my intermediate for loop
result there or there exists more elegant way to do this?
(code is added in the thread)
For the code:
(def text-nest [[[[]]] []])
(defn gen-level-1 [col level]
(if (coll? col)
(do (prn level)
(for [x (range level)]
[:span "$"])
(map #(gen-level-1 %1 (inc level)) col))))
(gen-level-1 text-nest 0)
I have figured out an alternative answer:
(defn gen-level-5 [col level]
(if (coll? col)
(mapcat #(concat (concat (repeat level [:span]) [[:br]]) (gen-level-5 % (inc level))) col)
[]))
(gen-level-5 text-nest 1)
;; => ([:span] [:br] [:span] [:span] [:br] [:span] [:span] [:span] [:br] [:span] [:br])
sean's answer is in the second to this message@s0930161 Screenshots are hard for us to read. Using a code snippet is easier -- and for a small piece of code like that you could just paste it in between triple backticks so it formats as code. As for the code and your problem, it's unclear what output you're expecting so it's hard to say what the code should do. Your (lazy) for
in that code won't do anything because, as you figured, do
returns the last expression -- the result of map
.
For that input -- [[[[]]] []]
-- what output do you want?
Thanks for your reply!
I added code to the thread of my original post.
My screenshot is meant to show the repl result.
[[[[]]] []]
in this case translate to 0 1 2 3 1
, and I would like to get (0 indent) (1 indent) (2 ident)
... and so on, namely getting different indentation based on the nested level of the location of that vector. (I used $ to represent indentation in my code)
Are you looking for something like this:
([:span 0]
[:span 1]
[:span 2]
[:span 3]
[:span 1])
If so, try the following code:
(defn gen-level [coll level]
(when (coll? coll)
(cons [:span level]
(mapcat #(gen-level % (inc level)) coll))))
(gen-level text-nest 0)
Happy to explain any and all of it.You don't need the for
loop because you're going to recurse down into the nesting automatically.
You need mapcat
to "un-nest" the results and join them together instead of map
.
Does that help @s0930161?
@seancorfield I believe [:span 2]
will generate something like <span> 2 </span>
. (correct me if I am wrong:sweat_smile:)
But I need to use the number 2 to generate two item like <span></span><span></span>
, which will look like having two indentation visually. So maybe I still need some looping do achieve this?
Ah, OK, so instead of cons
, you'd want something like (into (mapv (fn [_] [:span]) (range (inc level))) (mapcat ...))
@seancorfield I'll need to loop up mapv
a bit :rolling_on_the_floor_laughing: and think this through. Thanks again, Sean.
I guess I still don't understand what output you're trying to generate.
All you're going to get is a bunch of <span/>
tags. How does that relate to the nesting? What delineates each group of spans?
I generated this:
[[:span "$"] [:br]
[:span "$"] [:span "$"] [:br]
[:span "$"] [:span "$"] [:span "$"] [:br]
[:span "$"] [:span "$"] [:span "$"] [:span "$"] [:br]
[:span "$"] [:span "$"] [:br]]
just as an example of nesting and delineated spans, from this code
(ns stagmoose)
(def text-nest [[[[]]] []])
(defn gen-level [coll level]
(when (coll? coll)
(into (mapv (fn [_] [:span "$"]) (range (inc level)))
(cons [:br] (mapcat #(gen-level % (inc level)) coll)))))
(gen-level text-nest 0)
my original intention is to build something like text editor and I want to put my item inside the nested vector so `[[1 [3]] [2]]` will look like:
- 1
- - 3
- 2
so I have to decide the item's level and output the corresponding indentation to make my data look good.How close does my output above come to what you need?
Where I have (when (coll? coll) ..)
you probably want (if (coll? coll) .. [:span coll])
to generate the content maybe
Like I say, it's really hard to understand what output you're trying to produce based on what you've said so far.
But I think some variant of my gen-level
function above is pretty close?
It's nearly 10 pm here so I'll be gone soon, but feel free to ask more Qs, either in this thread or in the channel and I'll ask them later/tomorrow!
Hi, I have a question about reloading with ring. I’ve used wrap-reload
for hot-reloading. It seems if file was changed, reload modified-namespaces. But when I added wrap-reload
without main-entry and run it with REPL but It’s not working. I got an error “address in use”. so I added main-entry to run reloadable app and run the program using main-entry, and It’s working. Why do wrap-reload
need main-entry? and How can I know it need this?
here is the code that is working
(ns hello-world.core
(:require [ :refer [copy file]]
[clojure.core :refer [bean]]
[ring.adapter.jetty :refer [run-jetty]]
[ring.middleware.reload :refer [wrap-reload]]
[ring.middleware.params :refer [wrap-params]]
[ring.middleware.multipart-params :refer [wrap-multipart-params]]
[ring.util.response :refer [response]]))
(defn handler [{params :params}]
(prn "test")
(response ""))
(def reloadable-app (wrap-reload (-> handler
wrap-params
wrap-multipart-params)))
(defn -main []
(run-jetty #'reloadable-app {:port 3000
:join? false}))
not working one is without (defn -main [] ())
and I loaded file in REPL
"not working one is" may be because port 3000 was already in use when you try to run from REPL. In second case it just reloads the changed file.
I expected same thing between REPL and main-entry like reloading changed without restart (REPL or main). I’ve just run only in REPL but same.
(ns hello-world.core
(:require [ :refer [copy file]]
[clojure.core :refer [bean]]
[ring.adapter.jetty :refer [run-jetty]]
[ring.middleware.reload :refer [wrap-reload]]
[ring.util.response :refer [response]]))
(defn handler [{params :params}]
(prn "test2323")
(response ""))
(def reloadable-app
(-> handler
wrap-reload))
(run-jetty #'reloadable-app {:port 3000
:join? false})
"test"
2021-08-20 15:06:24.932:INFO:oejs.Server:qtp1546265708-28: jetty-9.4.40.v20210413; built: 2021-04-13T20:42:42.668Z; git: b881a572662e1943a14ae12e7e1207989f218b74; jvm 16.0.1+9-jvmci-21.1-b05
2021-08-20 15:06:24.944:INFO:oejs.AbstractConnector:qtp1546265708-28: Stopped ServerConnector@912a3bf{HTTP/1.1, (http/1.1)}{0.0.0.0:3000}
2021-08-20 15:06:24.950:WARN:oejs.HttpChannel:qtp1546265708-28: handleException / java.io.IOException: Failed to bind to 0.0.0.0/0.0.0.0:3000
"test123"
interesting. I realized first attempt is okay for sure. and second attempt got error as I’ve seen. and third attempt works like charm. Between attempts I didn’t do anything only request to localhost.I've got a
sorry just learning to use slack
I've got a function (defn xmlToString [elem params contents] and if I pass in a vector with apply, that works: (apply xmlToString [:div {:style "color: blue;"} "boembabies"]) but if I pass in a vector of vectors with apply, things break: (apply xmlToString [:div {style "color: blue;"} [:p {} "boembabies"]) then I get "; ArityException Wrong number of args (4) passed to: makeworkflow/xmlToString clojure.lang.AFn.throwArity (AFn.java:429)"
Also: (apply xmlToString [:p {:style "color: blue"} [:div {:style "color: blue"} "boembabies"]]) Gives: ; IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Keyword clojure.lang.RT.seqFrom (RT.java:542)
looks like you are calling xmlToString recursively without checking its arguments. Look at the stacktrace. There should be more than one entries for xmlToString. In that case - add (prn "---DBG" elem params contents)
as a first expression in the body of xmlToString to see which arguments causes that exception.
this is the entire function: (defn xmlToString [elem params contents] "given the element name as a keyword, params as a dict, and contents" (prn "---DBG" elem params contents) (let [cont (cond (string? contents) contents (vector? contents) (join "" (map #(apply xmlToString %) contents)) :else "") kv (keyvalues params) kvs (cond (= kv "") "" :else (str " " kv))] (str "<" (name elem) kvs ">" cont "</" (name elem) ">"))) It seems the first invocation is causing this
I think I see, so yes you need double [] around the contents
thanks !
What is the idiomatic way to make the hash-map {:a [1 2] :b [3]}
from [[:a 1] [:a 2] [:b 3]]
?
(defn foo [args]
(reduce
(fn [acc [k v]]
(update acc k (fnil conj []) v))
{}
args))
You can also do it with conj
hm… how?
oh, sorry, I didn't see the merging req
I'm also curious if there is an even simpler way.
(into {}
(for [[k k+vs] (group-by first s)
:let [v (take-nth 2 (rest (flatten k+vs)))]]
[k v]))
Less idiomatic.Thanks, will have a look at it.
The most easy-to-understand way would probably be to turn every tuple into a map and use a merge function that adds the v to a list @ key k. Would be two simple commands. I am on a mobile now so can’t test in a REPL
there's also the xforms library:
(require '[net.cgrand.xforms :as x])
(into {} (x/by-key first (comp (map second) (x/into []))) [[:a 1] [:a 2] [:b 3]])
I figure that the idiomatic way is to copy the approach used in group-by
, except also applying a function to the accumulated data. The result of this is almost exactly what @U04V4KLKC wrote at the top of this thread.
(The one possible distinction is that this would use assoc
/`get` instead of update
with an fnil
on the conj
. This is a trivial distinction).
When clojure gives an error message how can I get the closest expression to the error ?
Often, you can look through the stack trace to find the deepest point where your own code is involved; that should point you to a source file and line number.
If you're doing something with lazy sequences things can get a bit tricky, but that's a good starting point.
I am trying to learn re-frame and I am getting confused looking at the templates source
(defn ^:dev/after-load mount-root []
(re-frame/clear-subscription-cache!)
(let [root-el (.getElementById js/document "app")]
(rdom/unmount-component-at-node root-el)
(rdom/render [views/main-panel] root-el)))
I don't understand ^:dev/after-load. I believe it is a reader macro to add meta information, but I don't understand the point of meta information. who uses it/consumes it?
the gist is "my code has changed, react should rerender the application again with the new code"
but here is the init function
(defn init []
(routes/start!)
(re-frame/dispatch-sync [::events/initialize-db])
(re-frame/dispatch-sync [::rp/add-keyboard-event-listener "keydown"])
(re-frame/dispatch-sync [::bp/set-breakpoints
{:breakpoints [:mobile
768
:tablet
992
:small-monitor
1200
:large-monitor]
:debounce-ms 166}])
(dev-setup)
(mount-root))
I just don't get it. I mean here, I am expecting (mount-root) to run, and you are saying that meta information can be used to bypass that?i don't think i said that? the metadata on mount-root
is what will ensure that it runs after every compiliation
@dpsutton I didn't mean to say you said that, I just understood you as saying that meta information can modify when a function gets called
its just information. shadow runs the compiler, outputs new js, presumably syncs it over a websocket to the browser, and then calls the function that has the metadata that says to call if after loading
it's not magic that somehow happens. that metdata is information to a clojure program, shadow-cljs, and indicates it should call it after loading code
so in this specific case, the ^:dev/after-load is a reader macro that acts like advice? https://en.wikipedia.org/wiki/Advice_(programming)
no. it is just a bit of information associated with the var mount-root
. It has no inherent value and doesn't affect the runtime or anything
user=> (defn ^:special foo [] (println "I'm special"))
#'user/foo
user=> (defn bar [] (println "i'm not special"))
#'user/bar
user=> (let [f (->> (ns-publics *ns*)
(filter (fn [[s v]] (-> v meta :special)))
(first)
(val))]
(f))
I'm special
nil
user=>
here i define a function with some metadata :special
(and ^:special
is just shorthand for {:special true}
. Then i look through everything in the namespace for the first one that is special and then call it
sparingly. and i think this is a fantastic use of it here. It's a bit of a special case, but you mark a var as a function you want called. If you are a beginner metadata really isn't something you need to worry about at the moment. It has uses and is easily over-relied upon.
@dpsutton meta seems extremely powerful, but when I was looking at it I just thought "oh, so you can use meta to say this always returns a string" or something very simple and one-use
if genprotobuf.clj in the current folder has a "(defn prototext ..." how do I import this? I'm trying to "(ns somens (:require genprotobuf :refer [prototext])" but this results in "; IllegalAccessError prototext does not exist clojure.core/refer (core.clj:4119)"
@oelewapperke Can you paste the full stacktrace into a Gist or pastebin or something like that and drop the link in here so we can see the full information?
@oelewapperke What is the ns
in genprotobug.clj
? The :require
needs the full ns name -- also needs [
..`]` around the arguments: (:require [genprotobuf :refer [prototext]])
(ns genprotobuf (:require [clojure.string :only [join split replace]]))
and the ns in the program file is: (ns makeit (:require [clojure.string :refer [join split split-lines]]) (:require [genprotobuf :refer [prototext]]))
If you change :only
to :refer
in genprotobuf
, does that make a difference?
I have seen below code , which returns a key, where or when such function will be helpful?
(defn client
[]
:clinet-key)
I'm having a hard time coming up with a good reason to do that.
Specifically having a zero-arity function for this is not likely to be useful in a way that's better than other, simpler alternatives.
For example, if for some reason you really want a global definition for this, written down in your code, you can do (def client :client-key)
But even then, I don't know what you'd DO with that. It feels pointlessly defensive.
If you wanted to have a key which is computed, you can do (defn client [param] (keyword (str "client-key-" param)))
or similar. THAT would make sense.
It could potentially be useful as a testing hook, if somebody is changing it out using with-redefs
. But that would be more idiomatic as (def ^:dynamic *client* :client-key)
/ binding
, most of the time.
That last point is debatable, though, especially if it's ONLY for testing. So that may be it.
Oh here's a possibility: maybe a different part of the program deals with thunks (functions of no parameters), and the execution time really matters. Some of them might have side effects, for example:
(defn bad-client [dir]
(fn []
( "rm" "-rf" dir)
:bad-client-key))
And then they would pass in either client
or (bad-client "/home/my-enemy")
to the thing that will be calling the thunk.There are many possibilities... not sure if this helps?
Hey all, can't tell if this is better in beginner or reagent, but here goes: Have a react component that needs JSON tree data, passed in as a prop, whats the best way to go about that?
ok so if it is edn and you are using a react component like :> Tree
you probably need the data to be javascript objects and not clojurescript data. And we're talking a bit loose here because EDN is a string format but i'm assuming we're actually dealing with the clojurescript values from that string