This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-09-08
Channels
- # announcements (32)
- # aws (2)
- # babashka (21)
- # beginners (143)
- # cider (3)
- # cljsrn (13)
- # clojure (65)
- # clojure-dev (7)
- # clojure-europe (20)
- # clojure-losangeles (8)
- # clojure-nl (13)
- # clojure-norway (39)
- # clojure-uk (9)
- # clojurescript (39)
- # code-reviews (10)
- # conjure (2)
- # cursive (3)
- # datascript (6)
- # datomic (40)
- # events (5)
- # exercism (23)
- # fulcro (9)
- # funcool (2)
- # girouette (2)
- # graphql (4)
- # helix (8)
- # improve-getting-started (4)
- # integrant (7)
- # introduce-yourself (5)
- # jobs (3)
- # luminus (32)
- # malli (3)
- # off-topic (10)
- # pathom (9)
- # pedestal (4)
- # polylith (25)
- # practicalli (1)
- # re-frame (4)
- # sci (3)
- # shadow-cljs (5)
- # tools-deps (25)
- # vim (31)
- # xtdb (32)
hey why does this function
(defn red [string]
(str "\\x1b[91m" string "\\x1b[0m"))
output this
because it has wrong ansi color codes?
(defn red [s] (str "\033[31m" s "\033[0m"))
nope didnt work
maybe theres something wrong with my terminal
@harsjoshi1614 you are escaping the backslash, causing it to be printed as is. I.e. (str "\\")
will print just \
. And then the string you are trying to use for color coding will just print as is. You want to first ditch the first \
and use a single one.
ya i did but clojure complained the \x is not an identifies escape sequence
so i used unicode and octal escape sequences that didnt work too
i guess it must be something wrong with my terminal
Yeah, and then after that it's about using something that's supported. \u001b and \033 should work.
I tried it in another terminal and it worked thanks !
And then if those don't work, it about the terminal you are using.
btw is there any way to escape hex codes ?
i always wonder is there a way to check if a terminal supports outputing colors like this ?
i mean programatically
you probably need to start digging into the terminfo database. The man page for infocmp
might be a good place to start 😉
what is the best way to return a java collection (any collection will do I guess) I guess I return a java.util.Collection and any clojure collection will do right
it's pretty easy to check at the repl
(instance? java.util.Collection [1 2]) ;; => true
vectors, lists and sets do implement java.util.Collection
, but maps don`tCan anyone explain me
(def fib-seq
(lazy-cat [0 1] (map + (rest fib-seq) fib-seq)))
I am from java back ground and not able to get (rest fib-seq) fib-seq)
is being calledlazy-cat
will concatenate lists laziy and since we initialize the lazy seq with [0 1]
, we can refer to earlier elemnts in the list to define the later elements. When we try and take more than 2 elements from the sequence we get to the (map + (rest fib-seq) fib-seq)
term (which is also lazy). Here map
will apply the function +
to the two lists it's given in sequence, and rest
will return the given sequence with the first element removed. So to calculate the third element, we apply +
to the first element of (rest fib-seq)
(which is 1
) and the first element of fib-seq
(which is 0
) to get 1
and include that in the sequence. Then the fourth element is 1 + 1
, the fifth is 2 + 1
and so on. Does that make sense?
I am a beginner too... so Please bear with me...
as fib-seq is defined as lazy it will not be evaluated when you define it.
It'll however get defined when you call upon it... e.g. (take 10 fib-seq)
When you do this... clojure env tries to figure out what the he*k is fib-seq... so it goes to the definition
(lazy-cat [0 1] (map + (rest fib-seq) fib-seq))
Since fib-seq is undefined at this stage... the map expression is an empty sequence so fib-seq gets defined as (0 1)
the next time it is called... we do have a value for fib-seq to compute inside map... so we compute
(map + (rest [0 1]) [0 1]) => (1)
followed by
(cat [0 1] (1)) => (0 1 1)
Repeat this again for the 3rd call.. you get
(0 1 1 2)
Finally at the end you get the long sequence
(0 1 1 2 3 5 8 13 21 34)
I like lazy constructs... it is the procrastinator's paradise!!😁Thanks for long write @U022LK3L4JJ and @U0P0TMEFJ, That helped one question I have is , does it in iteration? like take 0
take 1
take 2
till take 10
?
I'm not sure I understand the question. A lazy seq is a value in that once it's calculated an element, it's cached and won't be recalculated. So if you (take 10 fib-seq)
it will do the work to calculate the first 10 elements, then if you (take 11 fib-seq)
it will only need to calculate the 11th element.
ahh. I got that!!! thanks @U0P0TMEFJ
thanks @U022LK3L4JJ
👍 ... btw, what I said is not strictly speaking true, since clojure generally chunks generating lazy sequences for performance, but you can usually ignore that. If you find that you can't ignore chunking of lazy seqs, you're probably trying to use them for something they're bad at, like side effects ;)
Hi all, If a function called data
returns this:
[{:id 1, :title "a"} {:id 2, :title "b"} {:id 3, :title "c"}]
Can't I select the values like this? (let [{i :id t :title} (data )] (println "id" i) (println "title" t))
right now it is printing nil
Instead of ids and titles
How do I select the values with let?
vector of maps
(1 2 3)
How to deconstruct it then?
You can't, afaik. You need to map over data
:
(let [i (map :id data)
titles (map :titles data)]
...
Would be the simplest. If you absolutely want to do it in one pass, you could something like:But that would be calling the function twice
okay no worries
Store the data you get back. That's what let is for. Sorry, I thought that data was the result of the function, habit.
How to do it in one pass?
cljs.user=> (def data [{:id 1, :title "a"} {:id 2, :title "b"} {:id 3, :title "c"}])
#'cljs.user/data
cljs.user=> (map #(let [id (:id %)
#_=> title (:title %)]
#_=> (println (str id ". " title)))
#_=> data)
1. a
2. b
3. c
I don't get why you'd need that. If that's your need, I think you need to rething your data
function to serve you your data in the correct way. But otherwise, you can do it with map destructuring and a reduce :
(let [{:keys [i titles]} (reduce (fn [acc obj] (-> acc
(update :titles #(conj % (:title obj)))
(update :i #(conj % (:id obj))))) {:titles [] :i []} (data))]
(println i)
(println titles))
I don't caution this approach, it's ugly as hell imho. There may be a cleaner way to do this 👀
if you want to get all the id's in one list, and all the titles in another, this might help
(let [[ids titles] (apply map vector (map (juxt :id :title) [{:id 1, :title "a"} {:id 2, :title "b"} {:id 3, :title "c"}]))]
(prn 'ids-> ids 'titles-> titles))
The apply map vector
idiom is pretty cool too. You can almost think of it as rotating a matrix 😉
(->> [[1 2 3]
[4 5 6]
[7 8 9]]
(apply map vector))
;; =>
;; ([1 4 7]
;; [2 5 8]
;; [3 6 9])
so we can (map (juxt :id :title) ...)
to get ([1 "a"] [2 "b"] [3 "c"])
and then "rotate" the data by taking each element from each list in turn and making a vector out of them using (apply map vector ...)
... does that make sense?
Oh yeah, I just had forgotten that map
could take n collections as arguments. Thanks for the "think of it as rotating a matrix" tip 🙂
I guess it depends on what you mean by "in one pass"? Because map
and apply
are both lazy, it'll consume ram until the last element of the data is available, but sure, I guess there are 2 loops in there that are proportional to the size of the input list. Using reduce is probably the best way to cut the number of loops down .. as you mentioned 😉
macro writing situation/question. I have a macro that writes a let
block to determine if the third argument in the given sexpr is a map with a specific key, and then either uses that map or creates a new map, and then recreates the sexpr with the chosen map in the correct spot. it’s currently written to build two versions of the original sexpr call with an if
, meaning that one of the branches sometimes doesn’t have enough arguments for the function to the called which raises errors in my linter:
are the ;; current
and the ;; new
versions equivalent? from my small tests they are, but for some reason i’m seeing an error deep in the bowels of my code (stack overflow error) which is only triggered when i switch from ;; current
to ;; new
, even tho i don’t see why it should matter
Could I see an example use of the macro too?
;; with no map
(wait-for (draw state :corp 1 nil)
(system-msg state :corp async-result)
(effect-completed state :corp eid))
;; with a map
(wait-for (draw state :corp (make-eid state) 1 nil)
(system-msg state :corp async-result)
(effect-completed state :corp eid))
state is an atom, which is why this stuff looks “side-effect”-y
;; current
version produces:
(if use10175
(draw state :corp new10176 nil)
(draw state :corp new10176 1 nil))
and ;; new
version produces:
(clojure.core/apply draw state :corp new10186 (if use10185 (list nil) (list 1 nil)))
Okay, so in theory I think they should be the same. You can check for certain though by using macroexpand-1
on your usages with the old and new versions.
cool, then the error must be somewhere else. thanks!
Although I'll admit this macro does seem very odd to me. But maybe that's just because I haven't seen the rest of your game engine.
it’s very odd, lol. it’s a hand-built callback system meant to allow for non-blocking user input. i inherited this, i did not write it, lol
Sure, makes sense. Just this whole bit of "if the third thing is a map with an :eid key, act different" seems odd in particular.
Also I'll note your inner
gensym could be replaced with an auto-gensym
`(fn [inner#]
(let [~'async-result (:result inner#)]
~@expr))
i did that to avoid shadowing when nesting wait-for
calls. if i use an auto-gensym, each of those anonymous functions will share the same symbol name, which is technically fine but makes me nervous lol
Right, but you can't be using it inside your expr anyway which means shadowing isn't an issue
that’s a good point
I think the same goes for use-eid
and new-eid
... they can both be auto-gensym'd too ... right?
yes, i thnk you’re right
in this form (game.core.eid/make-eid ~'state)
do you mean to shadow state
like that? or could you get that from ~(second action)
?
We could do that, yeah!
if th
is passed a function (as in your example (make-eid state)
) won't it execute that function multiple times?
oh it’s very bad. someone new to clojure wrote this back in 2016 and it’s now being used in 604 places in our codebase, so most of my attempts to change/fix it have gone poorly lol
(defmacro wait-for
[action & expr]
(let [awaited-fn `(fn [inner#]
(let [~'async-result (:result inner#)]
~@expr))
th (nth action 3)]
`(let [th# ~th
use-eid# (and (map? th#) (:eid th#))
new-eid# (if use-eid# th# (game.core.eid/make-eid ~(second action)))]
(game.core.eid/register-effect-completed ~(second action) new-eid# ~awaited-fn)
;; current
#_ (if use-eid#
~(concat (take 3 action) (list new-eid) (drop 4 action))
~(concat (take 3 action) (list new-eid) (drop 3 action)))
;; new
(apply ~@(take 3 action)
new-eid#
(if use-eid#
(list ~@(drop 4 action))
(list ~@(drop 3 action)))))))
that should evaluate (make-eid state)
less often 😉i appreciate you reading it so closely tho, it’s really helpful. i’m still fairly new to macro writing and have trouble wrapping my head around it sometimes
you mentioned worrying about nesting wait-for
's ... I would have thought that writing ~'async-result
would have created problems with nesting
oh yeah, that is def something i worry about, but likewise i have no good way of handling it. an anaphoric macro is the best i’ve come up with for that
yeah, i want to expose the result of (draw state :corp 1)
(the drawn card) to the later functions:
(wait-for (draw state :corp (make-eid state) 1 nil)
(system-msg state :corp (str "drew 1 card: " (:title async-result)))
(effect-completed state :corp eid))
otherwise, i have to put the “result” somewhere on the state
map atom and then i have to deal with removing it at some later point (difficult, error prone) or leaving it on the atom forever (effectively a memory leak)
i mean, on the one hand i’m experimenting with rewriting the whole engine cuz this one is a mess and i hate working with atoms, but on the other hand, i don’t want to rewrite 42k lines, so anything less than that is a win
(wait-for [{async-result :result}]
(draw state :corp (make-eid state) 1 nil)
(system-msg state :corp async-result)
(effect-completed state :corp eid))
maybe? ....
(defmacro wait-for
[bindings action & expr]
`(let [th# ~(nth action 3)
use-eid# (and (map? th#) (:eid th#))
new-eid# (if use-eid# th# (game.core.eid/make-eid ~(second action)))]
(game.core.eid/register-effect-completed ~(second action) new-eid# (fn ~bindings ~@expr))
(apply ~@(take 3 action)
(if use-eid#
(list ~@(drop 4 action))
(list ~@(drop 3 action))))))
That’s very cool
my advice with macros is to do as little as possible in the macro and try and turn it into functions asap ... so I would try and get to something like
(defn wait-for* [[func st quay th & args :as action] side-effects]
(let [[eid args] (if (and (map? th) (:eid th))
[th args]
[(game.core.eid/make-eid st) (cons th args)])]
(game.core.eid/register-effect-completed st eid side-effects)
(apply func st quay eid args)))
(defmacro wait-for [nom bindings [func st quay th & args :as action] & expr]
`(wait-for* ~action (fn ~nom ~bindings ~@expr)))
(comment
(wait-for* [draw state :corp (make-eid state) 1 nil]
(fn [{async-result :result}]
(system-msg state :corp async-result)
(effect-completed state :corp eid)))
(wait-for something [{async-result :result}]
(draw state :corp (make-eid state) 1 nil)
(system-msg state :corp async-result)
(effect-completed state :corp eid))
)
where you've got an api that works with functions, and a macro just for syntax, no logic.... I've no idea if this works btw, just trying to be helpful 😉 ... but having made some terrible macro decisions in the past, I now try and make sure that I don't need the macro to actually do work ... just for syntax ... (also I gave it a name, so you can get clj-kondo to lint as defn just for fun 😉 ) @UEENNMX0Tto be fair, you could make the extras optional
(defmacro wait-for [ & body]
(let [[nom body] (if (symbol? (first body)) [(take 1 body) (next body)] [nil body])
[bindings body] (if (vector? (first body)) [(first body) (next body)] [[{'async-result :result}] body])
[action & body] body]
`(wait-for* ~(vec action) (fn ~@nom ~bindings ~@body))))
Hi all. Is the following understanding of the 'IDE' ecosystem correct? Static analysis (without needing REPL): • clj-kondo: static analysis and linting • clojure-lsp: a wrapper over clj-kondo, compatible with Language Server Protocol. Any editor that supports LSP can hook into clojure-lsp. • cursive: its own thing for IntelliJ Dynamic analysis: provides IDE-like features by evaluating via REPL • vim-iced for vim • conjure for vim • cider for emacs Mixed: Static + Dynamic • calva for vscode: uses clojure-lsp for static analysis • Tutkain for Sublime Text: can use clojure-lsp/clj-kondo for static analysis


This is a pretty good overview of many tools available from a vimmers perspective, if that helps 🙂 https://github.com/rafaeldelboni/nvim-fennel-lsp-conjure-as-clojure-ide
@UL6NS3E7P that sounds about right. note that you can use static + runtime together in one IDE
hmm. Any potential performance issues if I use clojure-lsp and vim-iced together with neovim?
I think iced overlaps with LSP in some ways for static / refactor tooling but not in a bad way. They should both happily handle different useful things 🙂
cool. I am using iced for now, and will add lsp when needed. Thank you all for the help!
I guess I feel obligated to add Tutkain for Sublime Text in the latter category (with a smattering of static analysis for highlighting locals). You can optionally use either clojure-lsp or clj-kondo for static analysis / linting.
After thinking on this a bit, I decided I am more comfortable with static analysis, and went ahead with clojure-lsp. Now I need to decide which tool to use to integrate REPL with vim. vim-fireplace, may be? I don't want to run into conflicts by mixing lsp and conjure/iced.
I'd second @U38J3881W’s recommendation above and adapt it to your needs. It provides a Neovim config using Fennel that wires up both clojure-lsp and Conjure. I've used Conjure as my primary Clojure dev environment for months now with no complaints: https://clojurians.slack.com/archives/C053AK3F9/p1631114227261500?thread_ts=1631114104.260100&cid=C053AK3F9
Clojure-LSP is very configurable, so it is a simple Matt of turning features off if not needed. Conflicts tend to be in formatting and UI elements, if any occur. The Clojure-LSP website has lots of useful information about the features and clients (editors) https://clojure-lsp.io/clients/
Whatever you do, favor dynamic REPL over static all day every day. Having both is the best of both worlds, but the dynamic live programming is where its at
I just mean, don't only use static analysis, you'll be missing out on the best part of Clojure if you don't learn how to leverage the REPL for live programming. Which is a comment targeted at: > After thinking on this a bit, I decided I am more comfortable with static analysis, and went ahead with clojure-lsp Use both ideally, but its really worth taking the time to learn how to leverage a REPL in-IDE.
And maybe if it was not clear, they are always available in combination. In VSCode, Emacs, Vim, IntelliJ and all that, you'll all be able to have both a live REPL and some static analysis happening together. You can set them up to have only one or the other, but both is the dream. Cursive does both static and dynamic. Emacs does both static and dynamic. VSCode does both static and dynamic. Vim I'm not sure, but I'm under the impression it does both as well.
@U0K064KQV interesting. Does the community have a go-to guide or video on REPL driven development?
@U11SSJP2A I am a bit hesitant on adopting the guide that @U38J3881W pointed to, because it is in fennel. I already have nix as the ‘exotic’ element of my configuration, don’t want to add more. Also, the ‘how to use’ section in that guide does not give me confidence that this configuration will integrate smoothly with my existing neovim configuration.
I recommend Stu Halloway's talk on REPL-Driven Development as a good starter. I gave a long talk/demo to London Clojurians also called REPL-Driven Development (Clojure's Superpower) and I have a couple of short videos about it on my YouTube channel.
If you're willing to pay for material, Eric Normand's http://PurelyFunctional.tv has an incredible REPL-Driven Development course.
Stu's talk: https://vimeo.com/223309989
My YT channel: https://www.youtube.com/c/SeanCorfield_A
Good material from Practicalli on this topic: https://practical.li/clojure/repl-driven-devlopment.html
Eric's course https://purelyfunctional.tv/courses/repl-driven-development-in-clojure/
https://clojure.org official docs on RDD: https://clojure.org/guides/repl/enhancing_your_repl_workflow and subsequent pages
I've found this 2min video shows nicely what's cool about it: https://www.youtube.com/watch?v=rQ802kSaip4
Just try installing vim-iced: https://liquidz.github.io/vim-iced/#installation (since you said your a neovim user)
@U0K064KQV how do you achieve linting? I see that neither iced (https://github.com/liquidz/vim-iced/issues/355) nor conjure (https://github.com/Olical/conjure/issues/68) want to support linting outright. Both recommend clj-kondo for linting.
@U04V70XH6 (I always read your last name as cornfield! Sorry for that) Thank you for the links! I had already gone through your video a few days ago. I am halfway through Stuart’s talk, and it’s enlightning in many ways.
The official website is very good at hiding away quality material.
> Clojure-LSP is very configurable, so it is a simple Matt of turning features off if not needed. This looks like the way to go @U05254DQM I don’t have the screenshots to back me up, but clojure-lsp, at least with its default configuration, failed to find documentation for a few symbols (external library like clj-time, Java symbols, etc.), while iced found them for me. If anyone has any idea on what’s going wrong wrt clojure-lsp here, please let me know. For reference, my project uses deps.edn.
Ya, just install clj-kondo as well for linting using your preferred choice as explained here: https://github.com/clj-kondo/clj-kondo/blob/master/doc/editor-integration.md#vim--neovim
And you can set vim-iced to use static analysis from clj-kondo too as explained here: https://liquidz.github.io/vim-iced/#static_analysis_clj_kondo