Fork me on GitHub
#clojure
<
2020-01-29
>
emccue01:01:31

@isak I implemented the defer version

emccue01:01:35

(defmacro with* [bindings & body]
  (when-not (vector? bindings)
    (throw (IllegalArgumentException. "Bindings must be a vector.")))
  (when-not (even? (count bindings))
    (throw (IllegalArgumentException. "There must be an even number of bindings.")))
  (if (empty? bindings)
    `(do ~@body)
    (if (= :defer (first bindings))
      `(try
         (with* ~(subvec bindings 2) ~@body)
         (finally
           ~(second bindings)))
      `(let ~(subvec bindings 0 2)
         (with* ~(subvec bindings 2) ~@body)))))

isak01:01:03

@emccue nice! I wonder why cursive doesn't like it. I guess only certain keyword patterns are allowed?

emccue01:01:19

There might be a way to finagle it

emccue01:01:36

it just doesn't recognize any bindings after the first :defer

cfleming01:01:54

This is the grammar Cursive uses:

(defn for-bindings []
  (vector
    (repeat
      (alt (all (keyword :matching :let :into :modifiers)
                (item :into :sub-bindings
                  (local-binding)))
           (all (alt (keyword :matching :when :into :modifiers)
                     (keyword :matching :while :into :modifiers))
                (any))
           (item :into :bindings
             (all (capture (alt (symbol :as :binding)
                                (vector :as :binding)
                                (map :as :binding)))
                  (destructuring)
                  (any :as :value)))))))

cfleming01:01:18

So it’s explicitly matching :let, :when and :while, not arbitrary keywords.

isak01:01:36

@cfleming ah, good to know, thanks

emccue01:01:55

@cfleming dumb question, but does that code for specifying a grammar live somewhere other than within cursive?

emccue01:01:22

(it seems like a solid dsl for specifying a macro form)

cfleming01:01:50

Not a dumb question at all! No, it doesn’t right now. I have a long-term plan to open up all the macro support so that users like yourself can add support for macros that you develop or use.

cfleming01:01:19

I’m planning to have an open-source repo that Cursive users could contribute macro support to, sort of like DefinitelyTyped for TypeScript.

emccue01:01:04

Sounds useful, I'm definitely interested when you are sure you have the public api down

emccue01:01:41

I can live with some undefined symbols for a few years if it means that the eventual api is good

cfleming01:01:03

Unfortunately I need to do some internal refactoring first, and I never get to that bit - there’s always something else that seems important. This was some of the first code I wrote in Cursive, and it definitely needs some love.

cfleming01:01:01

I’ve rewritten the form parser but haven’t integrated that work yet. But that will allow much better language support than I have currently, things like allowing macro forms to customise how the completion works at particular points in the forms (e.g. in a :keys destructuring only offering completions based on known keyword names, or in a :refer in an ns only offering symbols from the right namespace)

8
kenny16:01:19

That :keys completion would be so cool!! I have long been considering switching to the {:keys [:a]} syntax purely for the keyword completion. The whole rest of the code base doesn't use that syntax though...

λustin f(n)03:01:45

Is there any advantage to use clj + deps.edn over lein + project.clj? I am familar with lein and want to know if it is worth getting used to deps.edn in the future.

seancorfield05:01:25

@austin021 A data point: we started with lein back in 2011 at work because that's all there was; we switched completely to boot in 2015; and we switched completely to CLI/`deps.edn` in 2018. Happy to chat via DM in more depth about it any time. I contributed quite a bit to that ClojureVerse thread that Andy posted.

λustin f(n)05:01:13

Yeah, I looked through it and saw that! I will let you know if I have some questions. For now, I am looking at these options while doing a side project. So I am going to take the opportunity to try out deps.edn

seancorfield05:01:13

I highly recommend everyone at least try the new CLI/`deps.edn` stuff as modern alternative to lein/`boot` -- especially since it's official and part of the documented "getting started" process on http://clojure.org 🙂

seancorfield05:01:36

As the author/maintainer of clj-new and depstar, I'm happy to answer any Qs on it 🙂

mloughlin09:01:54

I'm yet to find a downside to using deps.edn as a new-ish Clojurian

mloughlin09:01:30

other than learning how to fiddle with a lein project file to follow tutorials

mloughlin09:01:49

boot is something that doesn't seem to come up unless an open source project uses it. It looks like a bit of an overlooked middle child (no offence, boot people!)

kelveden13:01:56

@U04V70XH6 I'm curious, why did you move away from leiningen in the first place?

kulminaator16:01:14

For me boot is a bit too dynamic, full of temptations

seancorfield16:01:20

When the CLI/`deps.edn` came out, I wrote a post comparing some aspects of the three tools https://corfield.org/blog/2018/04/18/all-the-paths/ but I haven't blogged about why we switched from boot (because it was mostly about problems we had with Boot's fileset abstraction and the pod system)

kelveden17:01:20

Thanks Sean, that's really helpful - I'll take a closer look later. I was most interested as I've been using leiningen exclusively for the 5 years or so that I've been using Clojure. I've attempted the move to deps.edn a couple of times but each time find myself back with trusty leiningen eventually.

seancorfield17:01:42

Happy to help any time! Feel free to DM me with Qs about it all if you want.

👍 4
dpsutton03:01:47

Quite nice in smaller footprint of tooling and way less machinery. Can easily target git deps rather than artifacts. On the build side lein is heavy but does a lot with that weight. Lein aims to be a project and dependency manager whereas clj is upfront about being just a class path builder and program starter.

Alex Miller (Clojure team)04:01:58

sure, use your metadata!

Alex Miller (Clojure team)05:01:56

(->> 'clojure.core ns-publics vals (map meta) (filter :macro) (map :name) sort) - something like that

Alex Miller (Clojure team)05:01:27

(-> ->> .. amap and areduce as-> assert binding bound-fn case comment cond cond-> cond->> condp declare definline definterface defmacro defmethod defmulti defn defn- defonce defprotocol defrecord defstruct deftype delay doseq dosync dotimes doto extend-protocol extend-type fn for future gen-class gen-interface if-let if-not if-some import io! lazy-cat lazy-seq let letfn locking loop memfn ns or proxy proxy-super pvalues refer-clojure reify some-> some->> sync time vswap! when when-first when-let when-not when-some while with-bindings with-in-str with-loading-context with-local-vars with-open with-out-str with-precision with-redefs)

Alex Miller (Clojure team)05:01:08

try doing that kind of thing in Java :)

seancorfield05:01:25

@austin021 A data point: we started with lein back in 2011 at work because that's all there was; we switched completely to boot in 2015; and we switched completely to CLI/`deps.edn` in 2018. Happy to chat via DM in more depth about it any time. I contributed quite a bit to that ClojureVerse thread that Andy posted.

grounded_sage08:01:37

Is there a way to tell when a core.async channel buffer is full or empty? I’m interested in using it to fill up entirely and flush entirely when it is full.

vemv11:01:01

A solution to the first question would lead to check-then-act race conditions > I’m interested in using it to fill up entirely and flush entirely when it is full. Sounds like a strange requirement. Say the max capacity is 500. It can transition from 450 (stored ietms) to 451 and also from 500 to 0? Seems too asymmetrical/abrupt A dropping or sliding buffer would be more typical usage

👍 4
grounded_sage08:01:31

Also is it okay using Thread.sleep inside a go block or is it more idiomatic to use a take on an async/timeout?

vemv11:01:43

It's not okay to do blocking IO within go blocks These two items can save you some headaches: http://danboykis.com/posts/things-i-wish-i-knew-about-core-async/ https://www.youtube.com/watch?v=096pIlA3GDo

grounded_sage12:01:40

Bookmarked. Thanks

noisesmith18:01:39

not just IO - any blocking op, including sleep

👍 4
jumar12:01:14

How do people typically deal with "feature flags" in Clojure? Currently, we just use standard configuration keys as feature flags and then have bunch of ifs in the code. This means they are global on/off (cannot be targeted to a particular user / customer segment) and requires application restart to name a few downsides. It's also relatively hard to manage them and actually not forget to remove them later. Do you have any tips for libraries and how to approach it in general?

vemv13:01:54

I tend to approach feature flags (or at least the kind you are describing) merely as an authorization problem. That way one doesn't reinvent e.g. clojure.core/if, or existing authorization techniques/libraries Maybe within an authorization-related function you can have some rollout logic (e.g. "is the user ID odd?"), but that's it (as opposed to a more pervasive approach)

vlaaad13:01:17

I read long time ago that feature flags should be not a boolean yes/no, but rather a percentage of affected users, so you can start rollout slowly and eventually increase it to all users

dharrigan13:01:26

Not using it myself (in my clojure project just yet), but I know the frontend guys are using https://github.com/Unleash/unleash

dharrigan13:01:55

There's a java client, should be easy to wrap or create your own version in clojure 🙂

vlaaad13:01:03

feature flags and ab-tests (segmentation) seem to be orthogonal

roklenarcic14:01:10

wasn’t there a function in one of the core namespaces for string length?

roklenarcic14:01:19

I forget what it’s called

Eamonn Sullivan14:01:27

(count <string>)?

roklenarcic14:01:38

I guess I can just use count if I don’t mind 4 or 5 branches being done first

roklenarcic14:01:43

I always used count but for some reason I thought that there was a function that worked on strings specifically and called .length directly

Eamonn Sullivan14:01:41

well, I think (.length <string>) also works. I'm too noob to accurately compare the two.

Alex Miller (Clojure team)14:01:39

unless you have proof that it matters, I'd use count

Eamonn Sullivan14:01:32

It looks like, from a glance at the source (https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java), that count is implemented as .length for strings, anyway. So, the same.

ghadi14:01:56

user=> (count nil)
0

ghadi14:01:06

(.length nil) -> 💥

Alex Miller (Clojure team)14:01:31

this is the kind of thing that the jit gets real good at optimizing so the perf is probably less of an issue than you think it is

aisamu14:01:03

Could you elaborate a bit on what you're referring to by "this kind of thing"?

Alex Miller (Clojure team)14:01:25

guessing what types/branches are taken in a particular chunk of code

👌 4
Eamonn Sullivan14:01:44

Thanks. That was the part I wasn't following in the original question, so didn't understand what was being asked.

Alex Miller (Clojure team)15:01:11

the jit keeps a statistical profile of call sites like this and can optimize the branch prediction

emccue16:01:37

@alexmiller silly question, but how good is Java's JIT at predicting map lookups over time, for instance in protocol methods provided by extend-type

Alex Miller (Clojure team)16:01:07

different case there - Clojure actually creates a per-call-site cache for protocol instantiations

Alex Miller (Clojure team)16:01:30

but I think it's just 1-slot iirc so monomorphic will be very fast but polymorphic will fall into protocol resolution

Alex Miller (Clojure team)16:01:54

generally direct implementations of protocols in deftype/defrecord/reify will be fastest (highly optimized by jvm), then external extension, then metadata instance resolution will be slowest

lambdam17:01:48

Hello, I was looking for a way to destrucutre a namespaced keyword defined with the shorthand syntax ::foo and figured that there is no entry for that in the documentation. Example:

(defn plop [{::keys [foo]}]
  (inc foo))

(plop {::foo 1}) ;; => 2

mccraigmccraig17:01:30

@dam

(defn plop [{foo ::foo}] (inc foo))
(plop {::foo 1})
does the trick

Alex Miller (Clojure team)17:01:28

Yeah, we have a doc site issue to enhance some of that. The ::keys will work in the same namespace

lambdam17:01:14

Yes thanks @mccraigmccraig. It was more for the case where you have multiple keys like {::foo 1 ::bar 2 ::baz 3} and that you don't want to repeat them all.

lambdam17:01:58

(defn plop [{::keys [foo bar baz]}]
  (+ foo bar baz))
for example

mccraigmccraig17:01:05

ah, yeah. i find i pretty much always prefer the longform destructuring syntax anyway because it nests and aliases nicely and i find code easier to read when there's just one type of destructuring around :man-shrugging:

Alan Douglass17:01:10

Has anyone been using https://github.com/sunng87/slacker with ring? I keep getting arity exceptions when I try the example

Alan Douglass09:01:38

I was using a too-old jetty, without async

andy.fingerhut22:01:47

So a Clojure protocol function can have a doc string. There is a library I am working with that has a protocol with several functions, and I am thinking about enhancing their doc strings to include notes on performance of the implementation. These notes are accurate for the implementation included in the library, but can at most be suggested targets if someone else chose to implement the protocol differently for another class later. Would it be reasonable to document the performance notes in the doc string as "for the implementation on class Foo, this function runs in O(n) time?" ?

noisesmith22:01:36

maybe "for the reference implementation on class Foo this function runs in O(n) time, other implementations should aim for the same" ?

noisesmith22:01:21

that way it's as much a guide to implementing the protocol as a doc of existing implementations