Fork me on GitHub
#beginners
<
2021-09-08
>
Harshit Joshi07:09:42

hey why does this function

(defn red [string]
	(str "\\x1b[91m" string "\\x1b[0m"))

delaguardo07:09:52

because it has wrong ansi color codes? (defn red [s] (str "\033[31m" s "\033[0m"))

Harshit Joshi08:09:55

nope didnt work

Harshit Joshi08:09:05

maybe theres something wrong with my terminal

Sampo Toiva08:09:12

@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.

Harshit Joshi08:09:48

ya i did but clojure complained the \x is not an identifies escape sequence

Harshit Joshi08:09:17

so i used unicode and octal escape sequences that didnt work too

Harshit Joshi08:09:32

i guess it must be something wrong with my terminal

Sampo Toiva08:09:43

Yeah, and then after that it's about using something that's supported. \u001b and \033 should work.

Harshit Joshi08:09:26

I tried it in another terminal and it worked thanks !

Sampo Toiva08:09:27

And then if those don't work, it about the terminal you are using.

Harshit Joshi08:09:47

btw is there any way to escape hex codes ?

Harshit Joshi08:09:08

i always wonder is there a way to check if a terminal supports outputing colors like this ?

Harshit Joshi08:09:19

i mean programatically

Ed08:09:36

you probably need to start digging into the terminfo database. The man page for infocmp might be a good place to start 😉

Benjamin08:09:13

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

Ed09:09:39

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`t

🤑 2
ghadi11:09:13

Maps implement java.util.Map

popeye09:09:54

Can 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 called

Ed10:09:07

lazy-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?

ChillPillzKillzBillz10:09:26

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!!😁

popeye12:09:49

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 ?

Ed12:09:24

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.

popeye13:09:54

ahh. I got that!!! thanks @U0P0TMEFJ

Ed14:09:35

👍 ... 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 ;)

finchharold09:09:22

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))

Maravedis09:09:12

What would this print ?

finchharold09:09:38

right now it is printing nil

Maravedis09:09:47

Yeah, that's pretty normal

finchharold09:09:52

Instead of ids and titles

finchharold09:09:21

How do I select the values with let?

Maravedis09:09:23

You have a vector. You want to select every :i and every :title ?

finchharold09:09:33

vector of maps

Maravedis09:09:43

Waht do you want in i ? [1 2 3] ?

Maravedis09:09:16

Because no, you can't deconstruct a vector with maps inside with this syntax.

finchharold09:09:31

How to deconstruct it then?

Maravedis09:09:55

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:

Maravedis09:09:15

Oh, sorry, work is calling, brb.

finchharold09:09:21

But that would be calling the function twice

finchharold09:09:27

okay no worries

Maravedis09:09:12

Store the data you get back. That's what let is for. Sorry, I thought that data was the result of the function, habit.

finchharold10:09:00

How to do it in one pass?

ChillPillzKillzBillz10:09:51

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

Maravedis10:09:55

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))

Maravedis10:09:44

I don't caution this approach, it's ugly as hell imho. There may be a cleaner way to do this 👀

Ed12:09:16

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))

Maravedis12:09:50

I knew there was a way to do that with juxt, but I couldn't figure it out.

Ed12:09:50

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])

Ed12:09:42

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?

Maravedis12:09:32

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 🙂

Maravedis12:09:28

Strictly speaking though, you do not do it "in one pass" 😛

Ed14:09:22

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 😉

tschady14:09:16

For original problem, ‘for’ is clean

Noah Bogart14:09:01

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:

Noah Bogart14:09:11

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

Joshua Suskalo14:09:36

Could I see an example use of the macro too?

Noah Bogart14:09:24

;; 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))

Noah Bogart14:09:54

state is an atom, which is why this stuff looks “side-effect”-y

Noah Bogart14:09:54

;; 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)))

Joshua Suskalo14:09:58

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.

👍 2
Noah Bogart14:09:37

cool, then the error must be somewhere else. thanks!

Joshua Suskalo14:09:01

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.

Noah Bogart14:09:16

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

Joshua Suskalo14:09:54

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.

Joshua Suskalo14:09:29

Also I'll note your inner gensym could be replaced with an auto-gensym

Joshua Suskalo14:09:05

`(fn [inner#]
   (let [~'async-result (:result inner#)]
     ~@expr))

Noah Bogart14:09:52

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

Joshua Suskalo15:09:06

Right, but you can't be using it inside your expr anyway which means shadowing isn't an issue

👍 2
Noah Bogart15:09:51

that’s a good point

Ed15:09:08

I think the same goes for use-eid and new-eid ... they can both be auto-gensym'd too ... right?

Noah Bogart15:09:55

yes, i thnk you’re right

Ed15:09:01

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) ?

Noah Bogart15:09:13

We could do that, yeah!

Ed15:09:58

also .. isn`t ~(cons 'list (drop 3 action)) the same as (list ~@(drop 3 action)) ?

Ed15:09:49

if th is passed a function (as in your example (make-eid state)) won't it execute that function multiple times?

Ed15:09:40

not sure if that's a problem for your code, but it looks like bad macro practice 😉

Noah Bogart15:09:52

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

Ed15:09:08

😉 ... we've all been there ...

Ed15:09:51

(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 😉

🎉 2
Noah Bogart15:09:01

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

Ed15:09:44

yeah ... it's a tough one to master ...

Ed15:09:04

that's why all the advice is "only write macros if you have to" 😉

😂 2
Ed16:09:59

you mentioned worrying about nesting wait-for 's ... I would have thought that writing ~'async-result would have created problems with nesting

Noah Bogart16:09:25

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

Ed16:09:07

is that cos you want to pass the result to the func in a random position?

Noah Bogart16:09:59

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))

Noah Bogart16:09:00

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)

Ed16:09:22

well ... it depends on how much refactoring you can get away with 😉

Ed16:09:54

usually you'd pass the bindings in

Noah Bogart16:09:19

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

Ed16:09:00

(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))))))

Noah Bogart16:09:25

That’s very cool

Ed16:09:07

Edited cos I made an obvious mistake 😉

Ed16:09:16

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 😉 ) @UEENNMX0T

Noah Bogart16:09:47

That’s very cool! Thank you so much for the help

👍 2
Ed17:09:42

to 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))))

jayesh-bhoot15:09:04

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

calva 2
👍 2
sublimetext 2
Maravedis15:09:54

static + dynamic*: calva for vscode.

👍 4
Olical15:09:07

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

👍 4
2
borkdude15:09:15

@UL6NS3E7P that sounds about right. note that you can use static + runtime together in one IDE

💯 2
👍 4
jayesh-bhoot15:09:56

@U04V15CAJ like calva does?

👍 2
jayesh-bhoot15:09:19

hmm. Any potential performance issues if I use clojure-lsp and vim-iced together with neovim?

Olical15:09:02

Not that I'm aware of. I'd highly recommend combining static and dynamic tooling.

👍 2
Olical15:09:31

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 🙂

Olical15:09:01

The Neovim LSP tooling has been fantastic for me so far!

jayesh-bhoot15:09:22

cool. I am using iced for now, and will add lsp when needed. Thank you all for the help!

flowthing17:09:28

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.

jayesh-bhoot17:09:46

@U4ZDX466T updated. Thanks.

👍 2
jayesh-bhoot17:09:32

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.

semperos18:09:37

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&amp;cid=C053AK3F9

practicalli-johnny20:09:14

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/

✔️ 2
didibus21:09:59

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

borkdude21:09:09

There isn't an either/or choice anywhere I think

didibus21:09:02

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.

👍 2
didibus21:09:22

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.

jayesh-bhoot05:09:57

@U0K064KQV interesting. Does the community have a go-to guide or video on REPL driven development?

jayesh-bhoot05:09:53

@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.

seancorfield05:09:12

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.

seancorfield05:09:46

If you're willing to pay for material, Eric Normand's http://PurelyFunctional.tv has an incredible REPL-Driven Development course.

seancorfield05:09:12

I really should make a blog post about this...

💯 4
didibus17:09:21

I've found this 2min video shows nicely what's cool about it: https://www.youtube.com/watch?v=rQ802kSaip4

didibus17:09:08

Just try installing vim-iced: https://liquidz.github.io/vim-iced/#installation (since you said your a neovim user)

jayesh-bhoot09:09:52

@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.

jayesh-bhoot09:09:00

@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.

jayesh-bhoot09:09:09

The official website is very good at hiding away quality material.

jayesh-bhoot10:09:57

> 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.

practicalli-johnny13:09:32

#lsp channel is very helpful in that regard.

👍 2
didibus17:09:22

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

👍 2
didibus18:09:16

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

👍 2