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)))))
@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)))
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).
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?
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.
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))))
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)))
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.
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.
Triple backticks -- but you need to change a setting in Slack so that Enter doesn't submit the partial code block as I recall.
Preferences > Advanced:
(or press Shift Enter inside a triple backtick code block I guess)
what is the use of var
https://clojuredocs.org/clojure.core/var when it is used?
I think it's very rarely used in that form. Most people use the reader tag #'
instead.
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.
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)
I got more info here https://stackoverflow.com/questions/39550513/when-to-use-a-var-instead-of-a-function
Calva only supports nREPL. I think there is an nREPL implementation for the CLR, but I haven’t tried it with Calva.
Given that you don’t care about performance, have a single, reasonably sized collection: Is map/filter/reduce composition generally prefered over list comprehension?
What’s the difference between map/filter/reduce and list comprehension?
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.
But in the most simple, ad hoc case I don’t typically need any of that.
If it’s simple enough to map/filter/reduce, compose it using that and a nice ->>
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
use transducers
So we use list comprehension when we want to express something more involved for clarity?
More when the simple things won’t work nicely without some seriously hard to write steps
Default to map/filter/reduce
for
is for when you need to make products of things, otherwise use map
That clicked for me
Ty all!
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
I’m interested but confused - what is the definition of list comprehension here? 🙂
It comes from math via https://en.m.wikipedia.org/wiki/Set-builder_notation
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?
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
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.
@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
.outerHTML
also provides a string representation
if i don't have a project.cljs do i have to create one or can i use the deps.edn?
I am trying to use cljsjs/mixpanel
but this doesn't work:
{: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"]}}}
You need to require mixpanel just like you’re doing for reagent or react charts .. you have them right there.
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")
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"}}
...
How are you running your project ?
Also https://betweentwoparens.com/blog/what-are-the-clojure-tools/
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)))
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.
if you make x an argument to the function you would achieve this. then just call foo
with whatever value of x you like
(defn foo [x]
(let [y 3]
(+ x y)))
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.
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
I see, thank you
do function docstrings support variable expansion? (def timeout 5000) (defn func (str "I'll wait " timeout "in this func...") [x] ...)
No they don't
They do when use ^{:doc ...}
user=> (defn ^{:doc (str "foo" "bar")} dude [])
#'user/dude
user=> (doc dude)
-------------------------
user/dude
([])
foobar
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.
thanks, was a passing curiosity while creating one
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?
pass arguments, don't reach "outward" for global state from inside a function
in general
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?
@jakub.stastny.pt_serv https://clojure.org/reference/reader#_metadata
that's the specific part about metadata, the rest of the document should explain what other special dispatch characters there are.
But can I define reader macros?
you can define data readers, which is not the same, but offers similar benefits
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
Data readers. Never heard of these. OK, will read up on it :thumbsup:
that is described in the same document I linked
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
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?@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)
These are great resources. Thanks a lot!
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?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.
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.
the difficulty starts when we want recursion