beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
2021-06-28T02:39:18.129700Z

Is this how one deals with local variables? Nested let's like this? (defn run [command & rest] (let [start-time (current-unix-time)] (print (str " " (colour :green "λ ") (colour :grey command)) "") (flush) (let [result (zsh command rest)] (let [duration (- (current-unix-time) start-time)] (format-duration duration (fn [duration] (println "... took" (str (colour :green duration) "."))) result)))))

pithyless 2021-06-28T12:59:33.166100Z

@jakub.stastny.pt_serv sometimes when writing imperative code (lots of side-effects and/or conditional short-circuits) I may reach for https://github.com/Engelberg/better-cond. Otherwise, just defining the steps in a single let (even with _) seems fine to me. I'd probably just move the formatting function out of the last form:

(defn run
  [command & rest]
  (let [start-time (current-unix-time)
        _          (do (print (str "  " (colour :green "λ ") (colour :grey command)) "")
                       (flush))
        result     (zsh command rest)
        duration   (- (current-unix-time) start-time)
        formatter  (fn [duration]
                     (println  "... took" (str (colour :green duration) ".")))]
    (format-duration duration formatter result)))

pithyless 2021-06-28T13:02:00.167200Z

it's hard to make side-effecty code look pretty in Clojure; which is great, because it makes you stop and think 10x if you shouldn't solve the problem in a different way (eg. better separating the functional and imperative shell parts and/or hiding some of the non-essential side-effect ugliness in a macro).

2021-06-28T14:10:21.171500Z

Thanks @pithyless. I'll definitely refactor at some point. Currently it's a quick and dirty shell script, it's not a big project. I do hate side-effects not being tidily isolated in one place. What do you mean by "hiding some of the non-essential side-effect ugliness in a macro"? That sounds interesting. Would you have an example?

pithyless 2021-06-28T14:20:02.172Z

Your code looks similar to the usage of clojure.core/time or clojure.core/with-open . Obviously, it doesn't make sense here (since run is just one function), but if you were thinking of this kind of logic "wrapping" arbitrary forms, it looks like something you may want to hide via a macro.

solf 2021-06-28T02:55:00.129800Z

You can make multiple bindings in a single let. In your example above, you can put the two last let together:

(defn run [command & rest]
  (let [start-time (current-unix-time)]
    (print (str "  " (colour :green "λ ") (colour :grey command)) "")
    (flush)
    (let [result (zsh command rest)
          duration (- (current-unix-time) start-time)]
      (format-duration duration (fn [duration]
                                  (println  "... took" (str (colour :green duration) "."))) result))))

solf 2021-06-28T02:57:15.130Z

And you can technically also merge the first let, although it wouldn’t be pretty as you need to call the first print before the rest of the code. Here’s a way, but it’s not recommended, I use it when I need to temporarily print something for debugging

(defn run [command & rest]
  (let [start-time (current-unix-time)
        _ (do (print (str "  " (colour :green "λ ") (colour :grey command)) "")
              (flush))
        result (zsh command rest)
        duration (- (current-unix-time) start-time)]
    (format-duration duration (fn [duration]
                                (println  "... took" (str (colour :green duration) "."))) result)))

seancorfield 2021-06-28T03:31:29.130300Z

And just in case you're wondering @jakub.stastny.pt_serv the _ is just a convention for "a local binding we don't care about". You'll see it a lot as a placeholder for unused arguments in functions.

2021-06-28T03:46:40.132400Z

Ah OK, perfect. I'm not too off then. @wizardsoft07 how do you make block of code in Slack? I'm on sure if it's just the iPad app, I was trying to go for 3 quasi-quotes, but it's not really working.

seancorfield 2021-06-28T04:11:10.132600Z

Triple backticks -- but you need to change a setting in Slack so that Enter doesn't submit the partial code block as I recall.

seancorfield 2021-06-28T04:11:52.132800Z

Preferences > Advanced:

seancorfield 2021-06-28T04:12:17.133Z

(or press Shift Enter inside a triple backtick code block I guess)

popeye 2021-06-28T06:03:59.133800Z

what is the use of var https://clojuredocs.org/clojure.core/var when it is used?

seancorfield 2021-06-28T06:07:14.134400Z

I think it's very rarely used in that form. Most people use the reader tag #' instead.

seancorfield 2021-06-28T06:09:07.136300Z

You'll see #' used in two main situations: 1. to introduce indirection so that code is more REPL-friendly -- see https://clojure.org/guides/repl/enhancing_your_repl_workflow#writing-repl-friendly-programs 2. to access private vars (often for testing, but sometimes for other purposes) since using #'some-ns/some-var gets around the private/public check.

👀 1
seancorfield 2021-06-28T06:10:44.136800Z

See https://clojure.org/reference/reader#_dispatch for equivalence between #'some-var and (var some-var) (also https://clojure.org/guides/weird_characters for a more general guide to Clojure's "weird" bits of syntax)

✅ 1
pez 2021-06-28T06:15:36.137Z

Calva only supports nREPL. I think there is an nREPL implementation for the CLR, but I haven’t tried it with Calva.

dgb23 2021-06-28T08:07:03.141200Z

Given that you don’t care about performance, have a single, reasonably sized collection: Is map/filter/reduce composition generally prefered over list comprehension?

lsenjov 2021-06-28T08:09:46.141600Z

What’s the difference between map/filter/reduce and list comprehension?

dgb23 2021-06-28T08:16:33.144800Z

The former (except reduce) returns lazy seqs or can be composed to transducers. The latter has special utilities to quasi iterate over multiple collections and to declare conditions.

dgb23 2021-06-28T08:17:32.146300Z

But in the most simple, ad hoc case I don’t typically need any of that.

lsenjov 2021-06-28T08:18:11.146800Z

If it’s simple enough to map/filter/reduce, compose it using that and a nice ->>

👍 1
lsenjov 2021-06-28T08:20:05.147800Z

Tbh I think I’ve had to use a for as a list comprehension maybe twice for a couple particularly complicated things, but 99% of the time you’ll probably be fine without it

octahedrion 2021-06-28T08:21:21.149300Z

use transducers

dgb23 2021-06-28T08:21:23.149400Z

So we use list comprehension when we want to express something more involved for clarity?

lsenjov 2021-06-28T08:22:15.150600Z

More when the simple things won’t work nicely without some seriously hard to write steps

lsenjov 2021-06-28T08:22:28.151100Z

Default to map/filter/reduce

octahedrion 2021-06-28T08:22:31.151200Z

for is for when you need to make products of things, otherwise use map

👍 2
dgb23 2021-06-28T08:23:04.151600Z

That clicked for me

dgb23 2021-06-28T08:23:10.151900Z

Ty all!

octahedrion 2021-06-28T08:25:52.152900Z

for is the thing that confuses most people new to Clojure, who naturally start using it as they would in Python, but in Clojure it has a different meaning

2021-06-28T09:40:41.153700Z

I’m interested but confused - what is the definition of list comprehension here? 🙂

2021-06-28T10:36:09.156300Z

It comes from math via https://en.m.wikipedia.org/wiki/Set-builder_notation

👍 1
🙏 1
sheluchin 2021-06-28T11:19:57.158300Z

I'm trying to sort out a DOM-related bug in my code. I can get the DOM element in my REPL after some browser interaction, and it looks something like #object[HTMLDivElement [object HTMLDivElement]] when I print it. I want to save this object to my tests so I can make assertions against it there, instead of having to interact with the browser. Could someone suggest how I could do that?

dgb23 2021-06-28T11:31:58.159700Z

if you want to test against DOM elements then you might want to create them, put them into a specific state and interact with them: https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement

sheluchin 2021-06-28T11:42:26.162400Z

I already have the element I'm interested in, so I don't think createElement is relevant. I can interact with the element in my REPL, but now I want to print it in my REPL in some format that I could copy/paste into my tests instead of a representation like this #object[HTMLDivElement [object HTMLDivElement]]. It seems like it should be a possible REPL-driven workflow.

jkxyz 2021-06-28T11:50:08.164300Z

@alex.sheluchin If you print an HTML element with (js/console.log el) most browsers will log a HTML representation of the object. You can copy that and convert it to e.g. Hiccup

dgb23 2021-06-28T12:06:58.164900Z

.outerHTML also provides a string representation

Simon 2021-06-28T12:15:11.165600Z

if i don't have a project.cljs do i have to create one or can i use the deps.edn?

Simon 2021-06-28T12:15:50.165700Z

I am trying to use cljsjs/mixpanel but this doesn't work:

Simon 2021-06-28T12:16:49.165900Z

{:deps {org.clojure/clojure {:mvn/version "1.10.0"}
        org.clojure/clojurescript {:mvn/version "1.10.773"}
        reagent/reagent {:mvn/version "0.10.0"}
        cljsjs/react-chartjs-2 {:mvn/version "2.7.4-0"}}
 :dependencies [[cljsjs/mixpanel "2.22.4-1"]] ; <--- THIS
 :paths ["src" "resources"]
 :aliases {:fig {:extra-deps
                 {com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}
                  com.bhauman/figwheel-main {:mvn/version "0.2.11"}}
                 :extra-paths ["target" "test"]}
           :build {:main-opts ["-m" "figwheel.main" "-b" "dev" "-r"]}
           :min   {:main-opts ["-m" "figwheel.main" "-O" "advanced" "-bo" "dev"]}
           :deploy   {:main-opts ["-m" "figwheel.main" "-O" "advanced" "-bo" "deploy"]}
           :test  {:main-opts ["-m" "figwheel.main" "-co" "test.cljs.edn" "-m" "move-nation.test-runner"]}}}

Lu 2021-06-28T13:03:04.168200Z

You need to require mixpanel just like you’re doing for reagent or react charts .. you have them right there.

Simon 2021-06-28T14:22:00.172200Z

I still get this error:

[Figwheel:WARNING] Compile Exception   src/movenation/analytics.cljs   line:3  column:5

  No such namespace: cljsjs/mixpanel, could not locate cljsjs_SLASH_mixpanel.cljs, cljsjs_SLASH_mixpanel.cljc, or JavaScript source providing "mixpanel" (Please check that namespaces with dashes use underscores in the ClojureScript file name) in file src/movenation/analytics.cljs

  1  (ns movenation.analytics
  2    (:require
  3     [cljsjs/mixpanel]
         ^---
  4     [cljsjs/firebase]))
  5  
  6  (defn init-analytics []
  7    (println "analytics initializing")

Simon 2021-06-28T14:22:53.172400Z

I have updated deps.edn as mentioned by @lucio to

{:deps {org.clojure/clojure {:mvn/version "1.10.0"}
        org.clojure/clojurescript {:mvn/version "1.10.773"}
        reagent/reagent {:mvn/version "0.10.0"}
        cljsjs/firebase {:mvn/version "7.5.0-0"}
        cljsjs/mixpanel {:mvn/version "2.22.4-1"}}
...

Lu 2021-06-28T14:35:15.172800Z

How are you running your project ?

Karo 2021-06-28T16:04:21.175800Z

Hi team, is it possible to mock the value of a specific local variable instead of the whole function response as in case of "with-redefs". For example I need to mock only the value of "x" in this function.

(defn foo []
  (let [x 5
        y 3]
    (+ x y)))

West 2021-06-28T16:09:21.175900Z

I'm not sure I understand what you're trying to do this for. Usually I would do (def x 5) and paste it into a repl. Or I would write it inline.

dpsutton 2021-06-28T16:10:29.176400Z

if you make x an argument to the function you would achieve this. then just call foo with whatever value of x you like

West 2021-06-28T16:13:06.176600Z

(defn foo [x]
  (let [y 3]
    (+ x y)))

Karo 2021-06-28T16:23:39.177100Z

we use "with-redefs" in unit tests in order mock response of a function, in my case I need to mock only the value of one local variable of the function not the whole function response.

dpsutton 2021-06-28T16:26:59.177300Z

the answer is you cannot. One way around this is to make that local an argument. Another way around that is to have that local be the result of a function call, and you can redef that function to inject a different value. There are many other ways around this, and those probably get into more structural changes to get out of this problem

Karo 2021-06-28T16:32:50.177500Z

I see, thank you

Franco Gasperino 2021-06-28T17:10:50.178900Z

do function docstrings support variable expansion? (def timeout 5000) (defn func (str "I'll wait " timeout "in this func...") [x] ...)

dpsutton 2021-06-28T17:16:43.179400Z

No they don't

borkdude 2021-06-28T17:23:45.179700Z

They do when use ^{:doc ...}

borkdude 2021-06-28T17:41:37.181300Z

user=> (defn ^{:doc (str "foo" "bar")} dude [])
#'user/dude
user=> (doc dude)
-------------------------
user/dude
([])
  foobar

Fredrik 2021-06-28T17:58:44.191Z

Borkdude's solution uses the metadata reader macro ^{:key val}, which assigns this as metadata to the next value read. The reason Borkdude's solution works is that function docstrings are simply the value of the :doc key of the metadata of the var pointing to the function. In other words, this is how (doc fn-name) works under the hood. Using ^:{doc x} is equivalent to supplying a docstring when the (string? x) = true, but for any other types of values the defn form will give you an error. Note that ^:{doc x} lets you assign any value to the doc of the metadata, not just values of type String , but it's probably best to stick to strings.

Franco Gasperino 2021-06-28T18:01:32.192Z

thanks, was a passing curiosity while creating one

Santiago 2021-06-28T18:06:24.195100Z

if I have “global variables” such as env vars or an atom used as shared state, is there some best-practice or nomenclature on how to make their origin visible inside functions? Should functions “always” get such variables as arguments instead of a direct reference inside their definition?

ghadi 2021-06-28T18:07:15.195600Z

pass arguments, don't reach "outward" for global state from inside a function

ghadi 2021-06-28T18:07:30.195900Z

in general

2021-06-28T19:23:16.196600Z

@borkdude wow that's cool! Thanks @frwdrik for explaining in detail.

2021-06-28T19:24:04.197800Z

I didn't know CJ has reader macros though? I searched for it some time ago, but didn't get much. Are there any good resources on the matter?

borkdude 2021-06-28T19:32:26.199200Z

that's the specific part about metadata, the rest of the document should explain what other special dispatch characters there are.

2021-06-28T19:32:32.199400Z

But can I define reader macros?

borkdude 2021-06-28T19:32:55.199900Z

you can define data readers, which is not the same, but offers similar benefits

borkdude 2021-06-28T19:33:54.202100Z

e.g. you could define a data reader foo/bar so you can write #foo/bar{:a 1} in your programs and your reader function will transform the literal value into something else

👍 1
2021-06-28T19:34:18.202300Z

Data readers. Never heard of these. OK, will read up on it :thumbsup:

borkdude 2021-06-28T19:34:32.202700Z

that is described in the same document I linked

Fredrik 2021-06-28T20:35:27.206Z

Arbitrary metadata in a function definition can also be supplied as a map before the arglist. So you could do (defn foo {:doc (str "bar" baz), :more-meta 123} []) , see https://clojuredocs.org/clojure.core/defn

greg 2021-06-28T22:48:16.212500Z

Hi, I'm trying to figure out some idiomatic way of transforming one data structure to another. This:

{:AAA {:prop1 ["A"] :prop2 ["a"]}
 :BBB {:prop1 ["B"] :prop2 ["b"]}}
Into this:
{:prop1 [:AAA ["A"]
         :BBB ["B"]]
 :prop2 [:AAA ["a"]
         :BBB ["b"]]}
Two question to you gents: 1. Is the custom reduce the only option here, or there is some existing building blocks, methods I could combine and use here? 2. Who do you usually tackle this problems when you haven't done such a type of transformation yet? I mean, do you go and implement it or you start checking first the Clojure cheatsheet and funs in libs such as medley and plumbing?

pithyless 2021-06-29T08:35:05.226100Z

@grzegorz.rynkowski Aside from helper functions everyone ends up writing (ala medley and plumbing), there are at least 2 libraries that define more declarative languages for describing data transformations: 1. https://github.com/noprompt/meander 2. https://github.com/redplanetlabs/specter I don't recommend pulling them into a codebase where a reduce and some helper functions will suffice (especially since they have a learning curve and add friction for future code readers), but it's good to know these kind of libraries do exist when you're doing lots of data transformations and maybe you're starting to lose the forest for the trees. (I'm also a big fan of transducers and https://github.com/cgrand/xforms when it comes to writing more composable building blocks)

👍 1
greg 2021-06-29T11:33:09.234400Z

These are great resources. Thanks a lot!

greg 2021-06-29T17:31:49.254100Z

I end up with a reduce:

(->> {:AAA {:prop1 ["A" "B" "C" "D"] :prop2 [1 2]}
      :BBB {:prop1 ["X" "Y"]         :prop2 [7 8 9]}}
     (reduce (fn [{:keys [prop1 prop2]} [k v]]
               {:prop1 (assoc prop1 k (:prop1 v))
                :prop2 (assoc prop2 k (:prop2 v))}) {}))

=> {:prop1 {:AAA ["A" "B" "C" "D"], 
            :BBB ["X" "Y"]}, 
    :prop2 {:AAA [1 2], 
            :BBB [7 8 9]}}
@pithyless I checked briefly the libraries you mentioned and in my case, meander sounds like something really nice. I watched one video and briefly checked the docs. I'm working on a side project that is heavy in data processing. So I juggle data and the idea behind meander sounds very appealing to me. Still, I tried to use it in my case and it wasn't obvious how to make such a transformation. On one hand it makes sense, but on the other it is yet another library, new notation, heavy in macros, therethrough not looks good using it from Cursive. What is your thoughts on meander? Is it worth to invest in it? Do you use it often?

pithyless 2021-06-29T18:16:54.254500Z

I think meander is a power-tool. Sometimes you just need a chisel, but sometimes you may find a use for a laser-guided-space-gun. If my current project was doing a lot more data juggling between formats, I would probably consider investing in it heavily (and doing the work of getting my team onboard). As it stands, I use it only occasionally but I know it's there - waiting to pounce. If I were doing data-intensive side-projects without needing to get others onboard, I may actually be using it more often now. I definitely see more use-cases for meander than the search capability of specter; but, again, the people behind specter are building compilers with it... so they're obviously getting their money's worth. I think @huxley may have more insight on whether to invest in meander; and also be sure to check out the #meander channel! There is an upcoming zeta branch that is also looking promising.

2021-06-29T19:24:20.254700Z

Meander is not difficult at all when it comes to basic stuff. I would say it is simpler than anything else. Often the code accurately reflects the structure of the data we are working on.

2021-06-29T19:25:44.254900Z

the difficulty starts when we want recursion