This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-04-17
Channels
- # announcements (1)
- # babashka (94)
- # beginners (76)
- # calva (24)
- # cider (24)
- # clj-kondo (1)
- # cljs-dev (16)
- # cljsrn (45)
- # clojure (135)
- # clojure-europe (9)
- # clojure-france (5)
- # clojure-germany (2)
- # clojure-italy (12)
- # clojure-losangeles (13)
- # clojure-nl (3)
- # clojure-portugal (54)
- # clojure-uk (20)
- # clojurescript (55)
- # conjure (67)
- # core-async (5)
- # cursive (2)
- # datomic (10)
- # docker (7)
- # duct (22)
- # emacs (16)
- # fulcro (34)
- # graalvm (15)
- # hoplon (1)
- # instaparse (1)
- # jobs-discuss (3)
- # juxt (94)
- # luminus (1)
- # meander (4)
- # off-topic (13)
- # pathom (4)
- # pedestal (1)
- # ring (3)
- # ring-swagger (2)
- # shadow-cljs (61)
- # spacemacs (17)
- # specter (2)
- # sql (23)
- # xtdb (33)
coding style question. to denote if an operation is successful, should we add the ? to the boolean flag? , e.g., success or success?
yup. just found this lengthy discussion: https://github.com/bbatsov/clojure-style-guide/issues/182
Not for me, personally. About the only thing that slightly annoys me about a Lisp 1 is remembering not to name parameters of functions so that they shadow common built-in functions, like map
etc.
Same for me. Naming map
as m
, count
as cnt
, reader
as rdr
seems unnatural. No way around it I guess.
key
is also a good one
I mean, it is not that you must avoid those names every single time -- it is avoiding them when you also want to call that function, but yeah, avoiding them all the time makes it less likely to accidentally use the wrong name when you do want to call one of those functions. The Eastwood linter can help catch such accidental uses of names, if you call them as a function.
@U0CMVHBL2 Do you use both clj-kondo and Eastwood? How much do they overlap and how much are unique?
I have not used clj-kondo, and do not know precisely how much they overlap. I know that borkdude has looked at some Eastwood features to inspire similar clj-kondo features.
Hi! I'm trying to extract a single project from a large monolithic codebase. What I'd like to have is something that, given a namespace, tells me all the namespaces that depend on it (so I can remove any ns outside this set). Can I do this in plain clojure, from the REPL? Or do I need some external analysis tool?
I think tools.namespace is created for this purpose. Never used it myself. https://github.com/clojure/tools.namespace
https://github.com/hilverd/lein-ns-dep-graph this could be a good starting point too
Thanks! I already tried to generate a graph and it was a huge tangled mess π Not the tool's fault thought: I think I have too many namespaces to make sense of any human-oriented visualization π I will check out tools.namespace!
I ended up copying part of code from https://github.com/hilverd/lein-ns-dep-graph and adapting it to my use case with tools.namespace. Thanks again!
I've built a small gist, for anyone finding themselves in the same situation: https://gist.github.com/setzer22/af1197b366f562408fae3e5aaa6a837a
Hi, hope everyone is doing well. I had occasion for the first time to want to 'clear out' a delay
. So, the next time I deref
'd it the underlying function would be re-run. Looking at it (https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Delay.java) it doesn't seem like there is such a mechanism. Did I miss something? Or maybe there is a better construct to use?
delays are immutable with a one time realization
sounds like you just want an atom with reset!, there's nothing that directly has the behavior you describe
@harold Perhaps this might do what you want? It's a variant/combination of atom
/`delay` that we use at work:
;; copyright (c) 2015 world singles llc
(ns worldsingles.lazy-atom
"Implements a variant of an atom that defers evaluation of its
initial value until its first dereference -- like delay -- but
can still be swapped/reset like a normal atom, allowing it to
act as a cache that can be reset and made to re-run that
initialization.
Usage: (:require [worldsingles.lazy-atom :refer [lazy-atom]])
(def thing (lazy-atom (some-expensive initialization)))
@thing ;;=> evaluates value on first use
;; then returns that value cached until
(reset! thing nil)
;; and now next dereference will re-evaluate that value")
(deftype LazyAtom [a init]
clojure.lang.IAtom
;; these are just pure delegation
(swap [this f] (swap! a f))
(swap [this f arg] (swap! a f arg))
(swap [this f arg1 arg2] (swap! a f arg1 arg2))
(swap [this f x y args] (apply swap! a f x y args))
(compareAndSet [this oldv newv] (compare-and-set! a oldv newv))
(reset [this value] (reset! a value))
clojure.lang.IDeref
;; this is the magic: if the atom's value is nil, reset it
;; using the initialization function (and return that value)
(deref [this]
(or @a (reset! a (init)))))
(defmacro lazy-atom
"Do not evaluate initial value until first deref."
[value]
`(->LazyAtom (atom nil) (fn [] ~value)))
Thanks @U04V70XH6 - this is a great start at codifying this idea. I appreciate the leg up.
TL;DR: It acts like a delay
based on the initial value. If you reset!
it to nil
, the next deref
will re-run the original initial value again.
(if you swap!
or reset!
it to anything else, it behaves like a regular atom)
(I should probably move this to https://github.com/worldsingles/commons so it's officially open source but you're the first person I've seen asking for that sort of thing)
Macro question, is it acceptable/reasonable/idiomatic for a macro to declare variables that are expected to be used by the resulting body? i.e. Iβm trying to reduce some boilerplate, and there is a function declaration in it like
(defmacro my-macro [& forms]
`(boiler
(plate
(fn [a b]
(boiler
(plate ~@forms))))))
Is that a reasonable/idiomatic thing to do? it feels slightly weird to have a
and b
βinjectedβ into the forms.
This ultimately is generated a handler for something, which is why that the (fn ...)
is for.it is generally frowned upon (you may find more info by searching for anaphoric macros)
i figured it was, but also figured i should ask
usually it's preferred to include the names you're making as part of the macro args
really, doing so is a timebomb that future you will probably regret :)
oh, ok so (defmacro my-macro [arg-1-name arg-2-name & forms)
then I would use it as (my-macro a b (+ a b))
ok. I definitely see the timebombliness
not really necessary, more just wondering how far to push it
is the form above still a βtimebombβ in your opinion? or just spitting names into scope the timebomb?
the latter is fine
there is only one case of this in core - proxy-super
's use of the magic this
symbol
the latter is fine
Sometimes it's nice to have implicit args, like how #() lets you implicitly use %, %1, etc. And so some people use it
or <>
sometimes as those placeholder names.
ok thanks! @alexmiller
and I'd point to as->
as an alternative to those forms that let's you name "it"
But that's rarer I'd say, and some people might still dislike it, I know some people dislike #()
ok thanks
#() is kind of a different case because it's reader syntax, not a macro
Ya, but to me it suffers from all the same issues mostly. You can't nest it because the names would be ambiguous, the user can't use the names % anymore either because they would clash
yep, that's the tradeoff. really the most common tradeoff in dsls is implicit (shorter but less obvious, reduces what's allowed) vs explicit (longer, but more flexible). where you draw the line is a matter of taste :)
[1 0]
vs {:x 1 :y 0}
(position has implicit meaning in the first one)
positional = syntax = implicit = more complex (combines position and meaning)
same thing with macros that introduce magic names
I feel like there's a function for this and I can't remember what it's called, but... is there a last
and butlast
combo like split-at
? (I know they're finite but count would just be another way to spell that without making an extra pass)
with a vector there's peek
and pop
user=> ((juxt pop peek) [:a :b :c :d :e])
[[:a :b :c :d] :e]
oh, hang on, these are function args so I guess they'll ... always? be an ArraySeq and so they'll be counted?
and so count
is not actually another pass
there is no contract that it is an ArraySeq
or at least, I can not think of any place such a contract is specified (even though it might actually be an ArraySeq in practice)
Yup, that's why I pointed it out. There's rare cases where the extra convenience might be worth the trade off for possible accidental name shadowing and having to know the implicit rules. Might be because I've been doing some Elisp lately as well. It think in mutable land, there is more often an it
that's clearly what you're going to operate over. On OOP they call it this
, so on those case for example I think it make sense.
Like say you had a (with-some-resource ...)
it would make sense to then say ok it
is implicitly going to be that resource. But then once in a while you want to nest those, and you want to have access to both the inner and outer resource? And you can't because it
is now shadowing. So ya, there's just a continuum of trade-off between convenience and clarity/utility
yeah, and I don't think that example is "bad"
culturally, I think Clojure programmers have decided generally though we prefer to draw the line to avoid that in general
in a narrowly scoped dsl, it could still be the right choice
Is there a Slack channel for using Clojure for GIS/Geo? If not, does anyone have any experience with this? I'm amazed at (A) how little I'm finding (factual/geo looks great but there's no real examples or public projects that seem to be using it) and (B) what I do find is really, really old and incomplete.
@abrooks I have not ever seen much about it
factual is the biggest company example I can think of too
I guess climate corp does a bunch of geo stuff, not sure if they have anything public
@alexmiller Ditto... but now I'm getting to. π I know I've seen people demoing projects using Clojure for GIS. I'm realizing that all of the cases I've seen are proprietary. I think there's just not much happening in FOSS / open projects.
what are you trying to do? there's definitely stuff out there for handling various geo formats
https://github.com/thi-ng/geom for geometry stuff
https://github.com/r0man/geocoder-clj or https://github.com/FarmLogs/geojson for data stuff
what happens to futures that go out of scope but are still running?
IIRC, they run to completion but you canβt deref the result.
ah, they also swallow exceptions and that's what was really going on. I've got it working now. I'm using a future as a way to do a bulk import asynchronously, so I don't need the result.
you can set the default uncaught exception handler to catch those probably
the future itself swallows the exception, so you either need to deref the future (which rethrows) or use try/catch
or maybe you just don't care about the exception Β―\(γ)/Β―
I've built a try-catch into the import now, yeah.
I have some code that uses bigdec here and there, sometimes I need to check for equality with another number (in my current case 0), but (= 0 0M) is false. Does someone has any advice to handle this kind of situations in big codebases?
Yes, thatβs what i was looking for, thanks @UJRDALZA5
zero?
Was looking for something that doesnβt rely on comparing with zero, applied to other numbers
==
user=> (== 0 0M)
true
Yeah Iβd usually use zero when I know for sure itβs zero, but in this case it was 0 because nothing got summed up, but could
In defprotocol
I know that it's not possible to have variadic functions. That being said, can there be functions with required keyword arguments (all required)?
e.g.
(defprotocol A
(foo [this {:keys [:bar :baz]}]))
(defrecord a
[stuff]
A
(foo [this {:keys [:bar :baz]}] ...))
^ is that possible?@nicholas.charchut That looks like a 2-arity function with the second one being a hash map.
indeed. however, the defrecord
fails to compile on something more complex than the minimal example above. It hadn't even occurred to me to try to get said minimal example because I thought it would fail. Thank you!
Keyword arguments -- & {:keys [the args]}
-- are inherently variadic so you can't do that on a protocol.
(you don't need the :
inside the [
]
BTW)
Is it possible to call another method within a defrecord
? that is,
(defrecord a
[stuff]
(foo [this] ...)
(bar [this] ... (foo this) ...))
You mean if you have a protocol with both foo
and bar
and you're providing implementations for both?
Yes. Given that you've defined both methods, how can one method call another in a corresponding defrecord
?
If you're in the same ns, that call to foo
inside bar
is calling the protocol which in turn will dispatch to the a.foo
implementation.
This is why it's often a good idea to have your protocols in a separate namespace, so the calls are explicitly into those versions.
Have a look at seancorfield/next-jdbc
for something that heavily protocol-based and implements several of them in terms of calls to others, e.g., https://github.com/seancorfield/next-jdbc/blob/master/src/next/jdbc/result_set.clj#L684-L690
That library has API functions that are variadic which delegate to the protocol functions (which are not). That's also a useful technique for fdef
(Spec) since you cannot spec protocol functions, only regular functions.
For example https://github.com/seancorfield/next-jdbc/blob/master/src/next/jdbc.clj#L226-L243
And the fdef
for it https://github.com/seancorfield/next-jdbc/blob/master/src/next/jdbc/specs.clj#L119-L123
Oh, I didn't know protocols couldn't be variadic. Seems like a weird restriction, are Java interfaces not allowed to be variadic as well? I guess I can't remember one ever being variadic, but varargs are rare in Java anyways
https://groups.google.com/forum/#!topic/clojure/AHfyzXCgvTk -- Stuart Sierra, back in 2011.
Multiple arities are supported, but not variadic. Even so, the recommendation is typically to write one arity in the protocol and provide a wrapper for multi-arity use (unless the different arities mean specific different things).
Hum, ok right, so not a limitation of interfaces, just that the feature isn't deemed worth implementing. Though I feel there's a piece I'm missing here. The low-level ness of protocol to me sounds like its the "use for performance" aspect of it. Which would be its fast dispatch. Wrapping the methods in a function seem counter-intuitive for that. At that point, why not keep using multi-methods?
@U0K064KQV Maybe do some performance benchmarking to satisfy your curiosity and answer that question? (I think I know what the answer will be but I don't care enough about that level of performance to bother doing those tests myself).
So I did some quick bench-marking. It seems direct protocol dispatch, through the protocol function is 33% faster than through a wrapped fn calling the protocol method under the hood. But the JVM seem to be able to optimize this, so that in hot-paths they both end up as fast.
But, they're both about 96% faster than using a defmulti with dispatch method being "class"
Thank you for confirming my hunch π And it also explains why that's a practice that members of the core team recommend.
Yup. In retrospect, I hadn't really thought about everything defmulti does, calls the predicate function, compare it one by one for equality against the options, etc. Hum, actually I'm not sure of the latter, maybe it does a hash lookup, but anyways, just calling class on some input is probably adding a ton of overhead
If the wrapper is just there for convenient arities and/or allowing instrument in dev/test, performance-sensitive code can still call the underlying protocol version directly (see the top-level next.jdbc
namespace API, for example), but performing that shortcut with multimethods is a lot harder (but still possible, I believe).
Multimethods definitely have their place -- when you need to dispatch on values and/or multiple types -- but that flexibility trades off against performance.
That's a good point, if the implementation needs to call a lot of protocols within itself, you can even skip the wrapping fn. But to be honest, I was surprised how little overhead it added, only 33%, and very quickly optimized away by the JVM.
(foo my-a {:bar 42 :baz "test"})