This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-03-27
Channels
- # aws (8)
- # beginners (172)
- # boot-dev (4)
- # cider (16)
- # cljs-dev (123)
- # cljsjs (4)
- # clojure (90)
- # clojure-brasil (3)
- # clojure-dev (7)
- # clojure-dusseldorf (1)
- # clojure-finland (1)
- # clojure-italy (59)
- # clojure-russia (3)
- # clojure-seattle (2)
- # clojure-seattle-old (1)
- # clojure-spec (40)
- # clojure-uk (28)
- # clojurescript (327)
- # clojurewerkz (3)
- # code-reviews (8)
- # cursive (4)
- # datomic (24)
- # editors (1)
- # emacs (19)
- # fulcro (147)
- # funcool (1)
- # graphql (1)
- # hoplon (34)
- # jobs-rus (1)
- # lein-figwheel (5)
- # leiningen (20)
- # luminus (14)
- # midje (1)
- # off-topic (8)
- # onyx (7)
- # parinfer (47)
- # pedestal (1)
- # perun (1)
- # portkey (46)
- # re-frame (25)
- # reagent (9)
- # remote-jobs (4)
- # ring-swagger (5)
- # rum (1)
- # shadow-cljs (113)
- # slack-help (8)
- # spacemacs (7)
- # sql (9)
- # tools-deps (23)
- # uncomplicate (3)
- # unrepl (3)
- # yada (6)
hey guys how come clojure is so terse? like all the variable names are extremely truncated.
(let [[f & rs] undo-values]
instead of first
and rest
?
It seems to be almost the exact opposite of Java style and many other languages
@noman those are variables and not defined by the language.
Also you don't want to shadow builtins with variable names for a few reasons.
i know they're defined by people but it seems all the clojure code i read does this, it seems like idiomatic clojure style
Clojure is a Lisp-1 and therefor variable and function names are in the same scope. Using first and rest here would prevent the use of those functions, and also with a proper editor, color your variable names differently to highlight that to the programmer.
but maybe that's why? cuz all the words are used up by functions so you have to get creative with naming?
Are you coming from Common Lisp?
Common Lisp is known to use very explciit variable names conventionally
clojure is my first foray into functional programming outside of functional javascript and functional java styles
@dpsutton i know, i know, it's just nice to see what smart people are doing at first, to emulate them
Oh well a fine choice then. Just let this fact slip by you and continue your learning. You'll understand a bit more later.
But it's always a good idea to write short functions that do one thing well, and use explicit variable names for self-documenting code. That's not to say documentation isn't a good thing though š
okay, i actually love writing descriptive variable names so it's nice to know that cryptic variable names is not a clojure convention
There are a few conventions in this regard though
For example, using coll
and pred
to denote a collection or predicate, respectively, is a convention used by some. This is because the signatures of the language itself do this for brevity, and Clojure programmers will have a better idea knowing what you mean if you stick to style conventions.
Google for Clojure style conventions. There's a few links with some overlap...take what you will from them all
There are some short symbol names that are fairly idiomatic but my advice is to use names that are clear and expressive. Read Zach Tellman's "Elements of Clojure" (ebook) for some really good advice about naming.
You will, however, see f
and g
for general function arguments (i.e., arguments that are functions). m
for a general hash map, l
for a general list, s
for a string, i
or n
for a number (int). And x
for a generic "thing", with xs
for a sequence/collection of x
. @noman
The more high-level and abstract and generic code is, the harder it is to give arguments "sensible" names.
You'll find examples of these very generic short names in the docstrings for core functions, e.g.,
user=> (doc comp)
-------------------------
clojure.core/comp
([] [f] [f g] [f g & fs])
Takes a set of functions and returns a fn that is the composition
of those fns. The returned fn takes a variable number of args,
applies the rightmost of fns to the args, the next
fn (right-to-left) to the result, etc.
This shows the x
and xs
convention -- for f
.
note that these conventions really apply to generic functions like you find in core
the bulk of your code is not generic - itās dealing with very specific things pertinent to your business
so youāre far more likely to have (defn active-contracts [db user])
or whatever
A pithy maxim Iāve heard is: the broader the type, the shorter the symbol
Wow thanks for the awesome explanations. That makes a lot of sense and also helps decipher the docstrings I've been reading
I have a design question. This is half āfor realā and half a thought experiment and a journey in learning clojure. Iām building a cli application in clojure and am trying to make the implementation extensible in various dimensions. One of the dimensions is the types of files the cli application should be capable of searching through. My initial implementation uses multimethods with something like:
(defmulti find-in-file-type
(fn [^File f _ _ _] (file-ext f)))
(defmethod find-in-file-type "jar"
[^File f display-name handler opts]
(...implementation))
(defmethod find-in-file-type "zip"
[^File f display-name handler opts]
(...implementation))
and since these different file types are supposed to be 1. added to 2. enabled/disabled from the cli I had the bright (and totally incorrect it turns out) idea of using function metadata for the defmethod
definitions above to describe the cli behaviour (say whether this file type is searched by default and what the cli help and options should call this file type etc).
Turns out meta data for defmethod
is a no go. My thought there was that a person adding a new file type would be able to just define another multimethod variant with some metadata and the cli parser would understand the meta and take care of the rest. I could have called (methods find-in-file-type)
and friends to introspect the types and produce correct command line help etc.
What would be a good way to model this as it seems that meta data on multi methods was not a good idea/not available?The way I understand it there is metadata on the defmulti
but not on the defmethod
which would invalidate my idea of using the meta for extending the cli interface
you can just return a function with meta, from your methods
(defmethod find-in-file-type "jar"
[f display-name handler opts]
^{:options []
:search-by-default? true}
(fn []
(do-things-here f display-name)))
obviously you need to call it, but you have the meta and the implementation, this way
another option is to just return a map from your methods, like {:fn ... :help ... :search-by-default? ...}
:man-facepalming: ok, that makes total sense. Guess Iām still getting used to the fu
lastly, multimethods are not the only way to extend programs...you can use a private atom and define your own function for adding methods to it, and then your own dispatch function
@joelsanchez sounds reasonable. Many thanks for the reality check. I think Iāll stick with multimethods for this round as a learning exerciseā¦unless there is a specific reason no to?
no, no specific reason, just mentioning the possibility š
I guess the atom approach would hide more of the implementation details and leave more freedom to do wheatever you want to internally
I guess both approaches essentially use global state, the atom becauseā¦well itās an atom, and multimethods in a more intrinsic way
just a style / personal preference thing imo š think carefully!
@joelsanchez many thanks, this was exactly the kind of answer I was looking for
by the way, defmethod
does this internally, in -add-method
: (swap! method-table assoc dispatch-val method)
hi there, I want to use react-leaflet and I need a way to learn about the interop of js-react with cljs-reagent. Anyone has some good resource on this?
that was what I was confused about: :>
found an explanation at https://reagent-project.github.io/news/news060-alpha.html
Someone here recommended me a good testing library and I forgot what it was. What's a good one?
That wasn't it
Ah nevermind. It was midje. Thanks.
Http-kit with a routing library?
So Iām just getting started with ClojureScript / Om.next / React. Iāve got a basic component up and running with a reconciler etc. Are there any popular choices for a library that handles hooking into the reconciler and talking to a server to request and synchronise data, or is that a roll-your-own thing?
@pooelfbin I would have looked under āHTTP Routingā or āWeb Frameworksā at https://www.clojure-toolbox.com/. One option would be for example compojure where you can start a compojure project (assuming you have leiningen installed) with:
~> lein new compojure myproject
...
~> cd myproject
~> lein ring server-headless
(compojure is a routing library as suggested by @joshua.d.horwitz above)I've seen a lot of code samples where in the requires section they put [clojure.string :as str]
. What if you need to use the actual str function? Do you just have to write clojure.core/str
?
@feihong.hsu (str arg)
should work as it is. Functions from clojure.string
like (str/split arg)
can be used too. The clojure runtime differentiates correctly based on how you call it.
The namespace alias isn't itself a valid symbol:
(require '[clojure.string :as stringy])
=> nil
stringy
=> CompilerException ...
So str
as above is unambiguous.well, that will match any fixed precision integer
hi, there's this function, that I don't understand yet. For such functions, how do you try to understand them? Do you go step by step? Is there such a debugger or similar tool? Here's the function:
(defn permutations [a-set]
(if (empty? a-set)
(list ())
(apply concat
(map (fn [x] (map cons (repeat (first x)) (permutations (rest x))))
(rotations a-set)))))
that's not very idiomatic code tbh
(defn tails [a-seq]
(if (empty? a-seq)
(cons a-seq ())
(cons (map (fn [x] x) a-seq) (tails (rest a-seq)))))
(defn inits [a-seq]
(if (empty? a-seq)
(cons a-seq ())
(cons (reverse (map (fn [x] x) (reverse a-seq))) (inits (reverse (rest (reverse a-seq)))) )))
(defn rotations [a-seq]
(if (empty? a-seq)
'(())
(rest (map concat (tails a-seq) (reverse (inits a-seq))))))
That repeat
means its argument (first x)
keeps getting cons
until there are arguments for the inner map, correct?
although I donāt think the repeat
means that. The repeat means thereās an endless lazy sequence of (first x)
lined up as the first arg to map.
@joelsanchez so in a more idiomatic way what changes would you propose?
sorry I'm at work but 1) use sets 2) use mapcat 3) most likely this is done better without cons
(zipmap?)
Is "Joy of Clojure" considered as an intermediate level book? I'm looking for a continuation for "Clojure for the Brave and True"
Intermediate-to-advanced I'd say. Perhaps it's good to read a couple of other books before Joy.
:thumbsup: for Joy of Clojure, if you've read Brave and true and played around some with Clojure it's a perfect fit IMO
I think Alex's Clojure Applied is great as well as Getting Clojure (more beginner-like but some really good stuff there) and Elements of Clojure.
Joy of Clojure is a great choice. Iād also humbly suggest Clojure Applied (I am a co-author).
[{:customer1 {:address1 {:lat :lon :contact} :address2 {:lat :lon :contact}}} {:customer2 ...}]
Should there be a map around each :customer
, or is that really a vector where it alternates between keys and values (poke it with (into {})
?)?
can you give example input and output? not super clear on what you're looking to accomplish
Hi Clojurians, anyone experienced interested in making a quick comparison of - duct
, pedestal
, luminus
?
Thanks :)
this is a sequence of maps. do you know how to get that information off of one of these maps? You know how to access map entries right?
I know how to destructure but the looping while destructuring is kind of difficult for me
make a function that destructures and makes a single object that you want. and then map that over your collection
This is horribly hacked quickly and there's probably a nicer way to build the map per customer, but:
(defn extract-details [customer]
{:slug (:slug customer)
:dropoff-address {:lat (get-in customer [:dropoff-address :point :lat])
:lng (get-in customer [:dropoff-address :point :lng])}
:pickup-address {:lat (get-in customer [:pickup-address :point :lat])
:lng (get-in client [:pickup-address :point :lng])}})
(map extract-details customers)
:point
already contains :lat
and :lng
so extract-details
can be simplified to
(defn extract-details [customer]
{:slug (:slug customer)
:dropoff-address (get-in customer [:dropoff-address :point])
:pickup-address (get-in customer [:pickup-address :point])})
Basically it maps over each element in the vector and creates a new vector where each element is the result of passing a customer to extract-details
always ask questions. happy to help! did we solve your issue? i hope i didn't make it sound like this was an "easy" thing
@U485ZRA58 Of course. For some reason I was convinced :point
had more than just those 2 keys in it... Don't know why
@timok, not sure if you've seen the threaded responses āļø
For thread-macro fun you can also do
(defn extract-details [customer]
(-> customer
(select-keys [:slug :dropoff-address :pickup-address])
(update :dropoff-address :point)
(update :pickup-address :point)))
However while that is less characters it's also probably less clear what exactly it's doing if you're first coming to it or coming back to it after some time away and don't quite remember the data structure, and I'm a fan of clarity š@timok When you are thinking you need a loop in another language there's a fair chance you need a call to map in Clojure. Think of map as taking the place of all those <collection> -> <transformed collection> loops.
hey, good morning guys
suppose I have 3 channels with a lot of activity
everytime I hit the `alts!!` I'm going to create a new timeout channel, right ?
is that the correct way to do ? the follow line will create tons of timeouts that will never be used .
am I wasting resource here ? how can I avoid that ?
(future
(while true
(let [[value channel] (alts!! [c1 c2 c3 (timeout 10000)])]
(some-computation-with-value value))))
I donāt think there is, but there is already a single thread dedicated to closing timeout channels when their (approximate) period is elapsed. Timeout channels are fairly lightweight.
You can close the timeout channel yourself, but I donāt think itās any cheaper than letting it close itself.
Iāve certainly written code much like this before and it seems eminently reasonable to me, assuming you do in fact want to do Something every 10 seconds even if you havenāt received a value on your other channels.
> when their (approximate) period is elapsed assuming the other channels we have a lot of activity. every 10ms Iām receiving one message. the loop will create every 10ms one timeout channel with 10s timeout
100 timeout channels are going to be created in just one second. 1000 timeout channels in 10 seconds that rarely are going to be used
@timok, not sure if you've seen the threaded responses āļø
You could write an atom-based timeout-style channel whose state updates with the last message received time, but honestly it seems like itād be about as much work as the provided timeout channel currently takes. But when in doubt, benchmark!
yeah @donaldball I will benchmark .. thx
How precise do you need to be about your 10s with no activity trigger?
Sure. In an atom or volatile, you could record the time you last received/processed a real message, and you could have a single wakeup channel that received a signal every e.g. x seconds. When it wakes up, it checks the time difference between now and the last real message received and if greater than 10 seconds, does whatever that thing is.
sure . exactly what I did.
Iāve created one thread
that will sleep for X seconds and wakeup and send a signal to one channel
thanks @donaldball
whats the ideal way to combine several vectors into one?
(= [1 2] (???? [[1] [2]]))
=> true
i think reduce concat seems ok, but it feels ok. curiuos what other options there are.@drewverlee you can use into if you need a vec instead of a seq
interesting. ill read over that
Is it possible to build up a vector from another vector? Map wouldn't work amazingly smoothly, as it would produce [a [nested thing]]
Roughly speaking, am taking what's in :arglist metadata (so [a b & {:keys [c d]}]
or similar)
And wanting to turn it into a b c d
In the least painful way possible
@daniel.gomme An argvector can be arbitrarily nested. Do you want to take an arbitrarily nested collection and return a flat vector of the symbols inside?
Oh damn, didn't realise that about argvectors. Yeah, I pretty much just want to extract all the symbols within an argvector out into one vector
something like (filter symbol? (flatten argvec))
maybe? usually flatten is a terrible choice but it kind of makes sense there
filterv if you really need vector and not lazy-seq
oh wait, flatten ignores hash-maps, so itās more complex than that
My util library has a function for this. You'd use it like (comfy/prewalk-transduce (filter symbol?) conj argvec)
I'll have a look! Though I'd like to find out the nice way of doing this in vanilla, still getting to grips with the language
Probably not a bad idea, though I don't think there's like an easy one-liner for this in the stdlib. Maybe try to do a recursive solution with a reduce.
ahh, cheers!
What is the convention for naming function parameters of a HOF in the case of more than one function parameter?
http://clojuredocs.org/clojure.core/juxt f1, f2... are also ok
Depends on the context. For a general HOF, I agree that f
, g
, h
and successors are appropriate, but the terse names only make sense when you actually make no (or very few) assumptions about the functions save that they are functions. If have more constraints, Iāve used the -fn
suffix eg test-fn
or generator-fn
to try and communicate that intent.
Hello everyone
I'm struggling to run a cljs repl with fighweel on node
Well, actually I finally made it, but I am a bit confused about how to load namespaces
How can I see the expansion of a macro I define in clojurescript? For example I do (from https://cljs.github.io/api/cljs.core/defmacro):
myproject.core> (defmacro str->int [s]
`(js/parseInt s))
#'myproject.core/str->int
but when I do a macroexpand
I get
myproject.core> (macroexpand '(str->int "foo"))
(str->int "foo")
Doing a macroexpand
of a built-in macro works fine (from https://cljs.github.io/api/cljs.core/macroexpand):
myproject.core> (macroexpand '(when true :foo))
(if true (do :foo))
Here are the specs for my REPL:
;; Connected to nREPL server -
;; CIDER 0.17.0snapshot (package: 20180203.535), nREPL 0.2.13
;; Clojure 1.8.0, Java 1.8.0_112
Thanks!OK nevermind I found a good explanation https://gist.github.com/mfikes/567c85c6473f2c4b511b