Fork me on GitHub
#clojure
<
2016-09-13
>
deiga00:09:30

Does anyone know if it’s possible to run just a single test or test class? It’s a bit tedious to run all tests for clojure everytime

Alex Miller (Clojure team)00:09:50

if so, lein help test will explain how to run single test namespaces or how to set up selectors for subsets of your test suite

deiga00:09:21

I’m not using lein for this, not sure if that’s even supported

deiga00:09:28

since half the stuff is java

deiga00:09:29

Trying to contribute to clojure is a bit difficult, with no setup instructions 😛

bfabry00:09:22

lein has test selectors, but there's no way that I've found to use them from the repl, so for me they seem like a bit of a non-starter. clojure.test/run-all-tests accepts a regex for namespaces, and clojure.test/run-tests will accept a list of namespaces. which I've found mostly sufficient

tanzoniteblack00:09:16

@deiga if it helps, all deftest macros create a function that you can call in the general fashion of calling clojure functions, which will report whether the tests succeeded or not

Alex Miller (Clojure team)00:09:29

oh, you’re actually talking about clojure itself!

Alex Miller (Clojure team)00:09:37

yeah, there is a how-to page

deiga00:09:48

Oh! Where? 🙂

deiga00:09:10

Lovely, ty ❤️

Alex Miller (Clojure team)00:09:22

the developing patches page has info on this

Alex Miller (Clojure team)00:09:39

under "Run An Individual Test"

Alex Miller (Clojure team)00:09:06

also see #clojure-dev here or the clojure-dev mailing list

Alex Miller (Clojure team)00:09:52

for places to discuss with people doing similar things

Alex Miller (Clojure team)00:09:52

what are you working on?

deiga00:09:49

I’m looking into clojure.xml. I had a problem getting the parsed XML as string, so I though I could add that. Also add some tests and maybe rewrite parse to use records since structmaps seem to be outdated

Alex Miller (Clojure team)00:09:43

ok, it’s unlikely we will do much with that though - while we’re not planning to remove it we also don’t have plans to spend time enhancing it. It seems like switching from structmaps to records would possibly be a breaking change too.

Alex Miller (Clojure team)00:09:35

generally it’s best to discuss these kinds of things on clojure-dev first before working on them

deiga00:09:38

I’ve been trying to get into the google group where I was supposed to open a discussion about this. But my name still hasn’t appeared on the contributers list so I can’t get in there. I’d rather work on something and then scrapping it than just wait around 😛

hiredman00:09:18

it uses defrecords and because it isn't part of the core language, active development is a little easier

deiga00:09:40

hiredman Yeah, I have. It seems to be a bit stale though

hiredman00:09:44

the release notes were updated 15 days ago

deiga00:09:39

And 9months before that

hiredman00:09:21

clojure.xml was last touched in 2010

deiga00:09:54

fair enough

deiga00:09:14

but it’s at least in the core library

deiga00:09:05

I’d rather spend my efforts to improve the language core than work on some external library

hiredman00:09:17

I understand the impulse, but it is extremely unlikely that any work on clojure.xml will actually get merged

hiredman00:09:37

you are likely looking at years of watching the patch go no where in jira, there is no guarantee a patch for data.xml wouldn't do the same thing, but it is slightly less likely

deiga00:09:08

alexmiller: You say something about not planning to spend time enhancing clojure.xml, how much time can it take to look over a contribution and merging that?

deiga00:09:47

hiredman: years?

Alex Miller (Clojure team)00:09:04

particularly in opportunity cost of not working on something else we consider important

Alex Miller (Clojure team)00:09:36

data.xml is where I would put any energy around xml

hiredman00:09:44

it can take years for an issue to be closed as wontfix

Alex Miller (Clojure team)00:09:02

well, I’ve been trying to be better about that :)

hiredman00:09:47

there was an issue in the clojure jira to make subs take negative numbers, the guy wrote a patch, it sat there for years, and then was closed as declined

hiredman00:09:53

dude was not pleased

hiredman00:09:37

that was sort of extreme case, but there are lots of similar little things

seancorfield00:09:33

As someone who actively maintains one contrib library (and less actively maintains another, plus occasionally helps out with others), I would strongly support hiredman’s suggestion that you direct your efforts at the contrib library for XML stuff @deiga and not worry about the core stuff.

seancorfield00:09:37

Changes to core are very carefully considered and reviewed which means they have to be much more conservative by definition. That’s what gives Clojure its amazing stability (and why we happily run Alpha builds in production all the time!).

hiredman00:09:07

my point with subs is not to argue about subs, but that was a pretty small change, it took years to be dealt with, so expect a larger change to take longer

hiredman00:09:57

and by years, I think it was around 3

hiredman00:09:40

it may have been like, there was an issue for it open for three years without a patch, someone opened a new issue, with a patch, and the patch was declined and both issues were closed

hiredman00:09:14

anyway, scale the mountain, but take an oxygen tank

deiga00:09:34

Eh, It’s kinda discouraging when one of the core team says to not bother

creese01:09:36

I would like to get a hash map to respond to clojure.core/name like a keyword. Suggestions?

Chris O’Donnell01:09:35

@creese could you give an example of input/output you'd expect?

richiardiandrea01:09:29

Mmm...I guess a spec where all the keys can be missing is not a very good one right? I can do: (s/def ::info (s/keys :opt-un [::name ::twitter-handle ::facebook-id ::domain ::telegram-token])) but then {:asd "asd"} is always valid, am I approaching the problem from the wrong angle?

Alex Miller (Clojure team)01:09:43

well that’s a perfectly fine spec and the opt-un will help it generate interesting values

Alex Miller (Clojure team)01:09:03

you could just do (s/keys) too to validate all the keys per their specs

richiardiandrea01:09:36

Thanks Alex, the generative part is working fine, maybe I am missing a way to validate that ::info can only have this spec and {:anything "bla"} is not a valid one

Alex Miller (Clojure team)01:09:06

maybe it will be valid in the future

danielcompton01:09:14

Is there a predicate that matches seqs, lists, vectors, and sets, but not maps? i.e. I want to be able to call seq on the collection and iterate over the values. map is the odd one out here

richiardiandrea01:09:40

sequential? maybe, not sure about sets though

Alex Miller (Clojure team)01:09:14

usually when I ask this question I end up changing my mind about needing that later though :)

danielcompton01:09:33

yeah I do wonder that too 🙂

Alex Miller (Clojure team)01:09:29

@richiardiandrea you can use an additional predicate to exclude all but a known set of keys but spec (and Rich) would encourage you to maybe not do that

richiardiandrea01:09:27

yes I was wondering if it was good practice, but I am using spec to validate data at the boundaries and I cannot avoid adding that check, or anything will pass through

amacdougall01:09:25

@alexmiller Thanks for your answer earlier about specialized sets! Good to know that my core objective was ill-advised. I found a better way of efficiently rendering my lines, fortunately, and if I ever do have to confront this use case, I will be better armed.

Alex Miller (Clojure team)01:09:59

@richiardiandrea that is one of the best places to need such a thing

richiardiandrea01:09:26

@alexmiller thanks for confirming!

Alex Miller (Clojure team)01:09:46

so (s/merge (s/keys) (s/map-of #{:name :twitter-handle :facebook-id :domain :telegram-token} any?)) will work

Alex Miller (Clojure team)01:09:03

or add the opt-un if you want generative

richiardiandrea01:09:26

yes I'll opt for opt-un 🙂

richiardiandrea01:09:06

@alexmiller if I may add a question, will this work in the 1.9.0 release?

(def ::info-keys [::name ::twitter-handle ::facebook-id ::domain ::telegram-token])
(s/def ::info (s/keys :opt-un ::info-keys))
It would be neat 😄

Alex Miller (Clojure team)01:09:29

This whole exact same conversation happened in #clojure-spec this morning btw :)

richiardiandrea01:09:46

oh too bad ... ok tnx, oh cool let me go there and read 😄

Alex Miller (Clojure team)01:09:12

No worries, but there is more explanation there

richiardiandrea02:09:51

btw this now works:

(s/def ::info (s/merge (s/keys :opt-un [::name ::twitter-handle ::facebook-id ::domain ::telegram-token])
                       (s/map-of #{:name :twitter-handle :facebook-id :domain :telegram-token} any?)))
(s/explain ::info {:twitter-handle "@trat" :asr "rst" :domain ""})
In: [:asr 0] val: :asr fails spec: :app.division/info at: [0] predicate: #{:telegram-token :name :facebook-id :domain :twitter-handle}
nil

amacdougall02:09:02

For everyone who has been answering all my dumb questions, you'll be glad to know that I'm at least making good progress on this map rendering business! Perhaps this SVG will render when I upload it...

amacdougall02:09:09

...okay, not particularly. Guess Slack doesn't recognize SVGs as images it can embed. Just take my work for it that it works! Eventually I'll get an actual web page up to render this stuff for live coding with Figwheel.

Alex Miller (Clojure team)02:09:52

I see it in Chrome! Nice.

chrisetheridge11:09:52

@rui.yang yep - Clojure has multimethods. different functions are called depending on a dispatch function http://clojure.org/reference/multimethods

kurt-yagram12:09:11

Having a protocol and dynamic var in ns1:

(ns ns1)
(def ^:dynamic *m* {:a 1 :b 2})
(defprotocol Protocol
  (f [arg]))
(extend-protocol Protocol
  clojure.lang.MapEntry
  (f [arg] (println *m*)))
and a second namespace ns2:
(ns ns2)
...
  (binding [ns1/*m* (conj ns1/*m* {:c "three"})]
    (f m))
I expect the map *m*, inside the binding, to contain 3 entries. However, there are only 2, the result of the (println *m*) being {:a 1 :b 2}, so the binding doesn't seem to work. Why is that?

kurt-yagram12:09:07

Or better, how to change the dynamic var temporary inside the scope of a function in another namespace?

darwin12:09:48

just curious, what happens if you do (bound-fn [] (f m)) instead of (f m) ?

darwin12:09:11

another idea, did you require ns1 inside (ns ns2)?

kurt-yagram12:09:20

yeah, I actually did require - I just removed all non-important code (well, at least, I think it's not important 🙂 )

darwin12:09:03

that bound-fn idea is stupid,

kurt-yagram12:09:20

got an error using bound-fn 😛

darwin12:09:48

I wanted to test if there is any async code between your (f m) and println, that would explain it

kurt-yagram12:09:23

well, there's really not much more code in between

darwin12:09:24

try to println something before (f m), after (f m) and after exitting the binding

darwin12:09:47

the code how you presented it here looks good to me

kurt-yagram12:09:42

hmmm weird... binding doesn't seem to work fine. Just a sec.

kurt-yagram12:09:31

oh, the binding does work in ns2

kurt-yagram13:09:00

but it doesn't on ns1

chrisetheridge13:09:07

can you show your ns2 code?

chrisetheridge13:09:12

or is it the exact same?

kurt-yagram13:09:39

(binding [ns1/*m* (conj ns1/*m* {:c "three"})]
  (println ns1/*m*)
  (-> ...
      ns1/f)

kurt-yagram13:09:08

the println here shows the conjed binding. the println inside ns1 shows the wrong one. There's nothing in between them.

kurt-yagram13:09:39

oh, got it...

kurt-yagram13:09:57

so I didn't add all important code:

kurt-yagram13:09:46

with:

(ns2
  (:require [ns1 :as n]))
I cannot do: (binding [n/*m*...] ...) but I should do: (binding [ns1/*m*...] ...) I really thought I could use the namespace alias. Seems I can't

brett13:09:37

Hi, I’m trying to create a spec for a vector of maps that insures uniqueness based on key of map. For Example, I’d like to insure the maps in below vector are unique by the :id key.

[{:id 1 :name “foo”}
 {:id 2 :name “bar”}
 {:id 3 :name “foo”}]

brett13:09:19

Below only works if complete maps are distinct

(s/coll-of map? distinct true)
This would fail, as the maps are distinct, but it’s not what I want because the id’s are not unique.
[{:id 1 :name “foo”}
 {:id 2 :name “bar”}
 {:id 2 :name “foo”}]

Alex Miller (Clojure team)13:09:52

you’ll need a custom predicate for that

brett13:09:27

passed to coll-of instead of map??

Alex Miller (Clojure team)13:09:11

no s/and’ed with the coll-of spec

Alex Miller (Clojure team)13:09:40

(s/and (s/coll-of map?) #(thing that verifies uniqueness))

brett13:09:07

ok, great, thx, I’ll look at that.

brett13:09:57

did you ever think of allowing functions to be passed to distinct, e.g. (s/coll-of map? distinct :id) would make distinct by :id.

Alex Miller (Clojure team)13:09:45

it’s an interesting idea, but it might be harder to make that gen automatically

brett13:09:09

understandable. I’ll see where I get with using (s/and as generating a sample set is one of my goals.

Alex Miller (Clojure team)13:09:46

you may also find it hard then :) you might need a custom generator with s/with-gen (or maybe the generated things will be unique enough that it doesn’t matter, not sure)

Madara Uchiha14:09:59

Does clojure have the concept of an interruptible function?

jazen14:09:21

You mean a generator?

Madara Uchiha14:09:46

Yeah, for instance

Madara Uchiha14:09:31

And it does so without blocking the thread.

Madara Uchiha14:09:55

This same behavior can (and is, when accounting for older browsers that don't support this syntax) be implemented with generators

amacdougall14:09:57

expected: (== (+ (:margin render-env) (* (:mazes.grid/x top-right-cell) (:cell-width render-env)) (* (:mazes.grid/x top-right-cell) (:cell-h-spacing render-env))) (:x top-right))
  actual: (not (== 920.0 919.9999999999999))

amacdougall14:09:59

Floating point inaccuracy is the devil. Is there a better solution than writing is-more-or-less-equal-i-guess? and using that instead?

ghadi14:09:05

generators could be nice to simplify certain boilerplatey patterns inside lazy-seqs

ghadi14:09:46

or reducible sources. costs in the compiler & runtime would have to be quantifiably minimal... Definitely possible

lvh14:09:11

I would like to encourage all of you to try Clojure 1.9.0 alphas. Spec is great, but the new checking of macros and many builtin forms is uncovering bugs in a lot of software. Most of these are super easy to fix, but that’s work that parallelizes well. Here’s an example: https://github.com/lvh/prone/commit/7ec6bc8ed6db151bed1b980ce7b05107c73eae76

ghadi14:09:13

Syntax aside:

(reify clojure.lang.IReduceInit
  (reduce [_ rf init]
    (loop [res init]
      (let [v (f)]
        (if (identical? v eof)
          res
          (let [res (rf res v)]
            (if (reduced? res)
              @res
              (recur res))))))))

(__generator_fn__
   [f eof]
   (loop []
     (let [v (f)]
       (when-not (identical? v eof)
         (yield v)
         (recur)))))

ghadi14:09:27

hypothetical generator ^

ghadi14:09:38

with standard yield semantics

lvh14:09:40

If a lot of folks give this a shot, then by the time 1.9.0 rolls around, a lot of newbies are going to have a much better time 🙂

lvh14:09:47

Can I add things to that as well?

lvh14:09:36

added prone 🙂

Alex Miller (Clojure team)14:09:14

maybe you’re just lucky :)

quoll14:09:58

=> (map #(.hashCode %) {0 0, 7 7, 1 1, 4 4, 6 6, 3 3, 2 2, 9 9, 8 8, 10 10})
(961 1185 993 1089 1153 1057 1025 1249 1281 1217)

Alex Miller (Clojure team)14:09:18

maps don’t use .hashCode

Alex Miller (Clojure team)14:09:25

they use hash (backed by hasheq)

quoll14:09:33

oh, cool. TIL 🙂

quoll15:09:05

=> (map hash {0 0, 7 7, 1 1, 4 4, 6 6, 3 3, 2 2, 9 9, 8 8, 10 10})          
(-1663711614 825985219 670845861 1457028219 -645429335 921681221 1199864066 -813835253 2145648727 1228926976)

quoll15:09:45

not sure how big the backing vector is, but I’m guessing that number ends up first

quoll15:09:18

(after a mod operation)

lambeta15:09:59

I am not sure how first works

quoll15:09:07

oh… coffee kicking in…. I should only be looking at the keys 😳

Alex Miller (Clojure team)15:09:17

it coerces its arg to a sequence, then takes the first element

Alex Miller (Clojure team)15:09:31

in this case that’s a sequence of map entries (2-element tuples)

Alex Miller (Clojure team)15:09:47

maps are unordered, so no particular order is guaranteed

lambeta15:09:56

user> (first { 7 7, 1 1, 4 4, 6 6, 3 3, 2 2, 9 9, 0 0, 8 8})
[0 0]

quoll15:09:51

The order is deterministic… if you know the algorithm. I was just trying to pick at it without looking at the code 🙂

Alex Miller (Clojure team)15:09:11

well of course, but you should not rely on it as it can (and has) changed

Chris O’Donnell15:09:51

if you want the order to be consistent, you could use a sorted map

quoll15:09:54

I know. I spent a week fixing people’s borked tests when Java changed their hash function from 1.5 -> 1.6

Alex Miller (Clojure team)15:09:22

well in this case it’s Clojure’s hash function that’s important (and it changed in 1.6)

Chris O’Donnell15:09:23

though I suppose your keys would need to be comparable

quoll15:09:37

this was in a Java project (you know which one) 🙂

quoll15:09:08

I think I saw a couple of tests fail when Clojure 1.6 came out too… luckily by that point people had learned (after all, Clojure devs at that point were usually pretty experienced Java devs first) so it wasn’t too messy

lambeta15:09:04

@quoll

(map hash (keys { 7 7, 1 1, 4 4, 6 6, 3 3, 2 2, 9 9, 0 0, 8 8}))
(0 -137604029 1392991556 -803074778 1795257809 -1556392013 -971005196 1887164919 -153401025)

lambeta15:09:22

I can’t guess the algorithm

alpheus15:09:06

Calling clojure.java.shell/sh in user.clj hangs. The same call in another namespace doesn't hang. Requiring that other namespace in user.clj hangs. There's something special about user at load time that I don't understand.

quoll15:09:07

well, hash tables usually order by (hash key), and then mod into the size of the table. The keys here are longs, and Java uses identity for the Long.hashCode (or they did? They still seem to). Based on that, I was wondering if an entry of [0 0] would end up at the start of the table, no matter what. That’s what I went looking for

quoll15:09:43

but Alex pointed out hash… which, now that I think about it, I think I knew that once, and then forgot

quoll15:09:31

I was just poking around for fun, to see if there was another explanation for [0 0] showing up first, other than pure randomness. Even if there were, Clojure 1.10 could change the ordering again. It was just navel gazing, since any expectation of ordering on a hash is The Wrong Thing™

lambeta15:09:02

sure. 👍

Alex Miller (Clojure team)15:09:00

@quoll there is no mod. entries in hash maps are stored in hash array-mapped tries and the sequence ordering is based on traversing that tree

dpsutton15:09:40

also, aren't maps ordered under 7 keys maybe, and then swap to an unordered implementation above that?

dpsutton15:09:49

so possible small examples mislead about ordering of map keys

Alex Miller (Clojure team)15:09:09

yes, it’s <= 8 although there is at least one case (maybe with literals) where there is an off-by-one and it’s actually 9 I think

dpsutton15:09:59

and presumably that's not spec'd and therefore subject to change? (trademark)

quoll15:09:34

ah, I had presumed that hashmaps were using a vector to get their persistence. OK… I now have code to learn from. Thank you

Alex Miller (Clojure team)15:09:13

array maps will produce sequences based on the order of insertion (as it’s just backed by an array)

Alex Miller (Clojure team)15:09:05

vectors are also backed by hash array-mapped tries, keyed by index

Alex Miller (Clojure team)15:09:18

it’s a little different but same idea

Alex Miller (Clojure team)15:09:58

is a good starting point

dpsutton15:09:03

at clojure/west, there was a guy who did an excellent presentation on reordering the way key/value pairs and pointers to other tries were stored in HAMT. Has there been any more movement on his work?

lvh15:09:35

@alexmiller Since we’re talking about insertion order; given what I understand of prefix tries, they wouldn’t have the attacker-triggrable pathological insertion behavior that prompted randomized hashing for Python, Perl… &c’s hash tables — right?

lvh15:09:40

I guess I should go validate that assumption though 🙂

dpsutton15:09:12

bummer. it was a great talk

Alex Miller (Clojure team)15:09:17

@lvh that is still a concern as there are hash collision nodes

Alex Miller (Clojure team)15:09:23

which devolve to linked lists

dpsutton15:09:23

i've been reading papers on HAMT as well. super cool

lvh15:09:27

ah, crud

Alex Miller (Clojure team)15:09:31

there’s actually a ticket about this

Alex Miller (Clojure team)15:09:49

not sure if you’ve looked at how Java handled it, but the details are pretty interesting

dpsutton15:09:06

got a link to that ticket or more info? I'd like to read that

Alex Miller (Clojure team)15:09:59

but it doesn’t have many of the details

Alex Miller (Clojure team)15:09:41

in Java 7 they started seeding hash maps so every map was basically hashing differently

Alex Miller (Clojure team)15:09:06

in Java 8 they undid all that and just changed the implementation to not devolve to linked list behavior on hash collision but instead to use a red/black tree

Alex Miller (Clojure team)15:09:19

a similar approach could be used in Clojure as well

Alex Miller (Clojure team)15:09:42

another workaround would be to wrap keys in a deftype that used an alternate hash function

Alex Miller (Clojure team)15:09:32

if you’re interested in this, tracking down the Java JEPs had a lot of useful info iirc

Alex Miller (Clojure team)15:09:52

haven’t thought about this in a couple years :)

dpsutton15:09:23

thanks for the info

Alex Miller (Clojure team)15:09:22

having looked at this stuff a bit I have a lot of appreciation for the apparent thought involved in Java’s choices, both tactically and strategically

sandqvist16:09:59

Alex, one of the reasons I moved from Java to Clojure was the HashSet/HashMap change in 1.8. It used to be that Set membership was defined by .equals() which had to be consistent with .hashCode(). Now they sometimes use the Comparable interface (HashSet is implemented using HashMap). We had code break on the 1.7->1.8 upgrade.

sandqvist16:09:18

The Javadoc for Set.contains still claims "More formally, returns true if and only if this set contains an element e such that (o==null ? e==null : o.equals(e))." which is not true for HashSet in all cases.

sandqvist16:09:48

Fortunately, Clojure has correctly working sets and maps.

Alex Miller (Clojure team)16:09:22

Clojure’s sets are based on = and hash

Alex Miller (Clojure team)16:09:41

of course, it helps that = is not something that is open for interpretation in Clojure :)

smw17:09:44

Is there an elegant way (say with threading macros) to do nested maps?

smw17:09:29

(map #(map #(get-in % :_source :ecr_code))) non-empty)

smw17:09:43

obviously nested function literals are disallowed

alpheus17:09:18

the fn form can be nested though

smw17:09:20

Ahh, and so maybe I just setup the anonymous functions to be argument compatible with threading macro?

alpheus17:09:55

sounds right

smw17:09:12

Thank you

pjstadig17:09:33

@smw usually if my map gets too hairy i rewrite it as a for

pjstadig17:09:00

in fact my rule is (which I think I inherited from technomancy) that if you're having to use function literals its already complicated enough for a for, being able to name your iteratee and do lets and whens makes a for much more readable IMO

linicks17:09:24

Is there a switch to lein run to watch the project files and recompile? I'm trying to learn Clojure and Pedistal, and would like to see my changes with out stopping and starting with each change. Thanks!!!

pjstadig17:09:02

@linicks nothing for lein run that I know of. There are some specialized tools for autotesting, or in the case of ring there's a reload middleware. Perhaps there's something like that for Pedestal? Otherwise you could hook up a REPL to your editor and run the Pedestal app from your REPL, then you can recompile code into the same process that Pedestal is running from.

linicks17:09:48

@pjstadig, Thanks for the tips! I'll start investigating in those directions 🙂

akiva18:09:38

@smw, as an alternate to @pjstadig’s rule, I usually just create defn-s outside of the map and call them that way. That way, you end up with flexibility, functions can be used elsewhere, and with good naming, the map becomes self-describing.

pjstadig18:09:37

@akiva yes, i agree that splitting out functions is also a good way to go. sometimes I find it hard to come up with good names for intermediate computations, but also a for can get to the point where it's doing too much as well.

akiva18:09:33

Agreed. Nothing wrong with a good for.

akiva18:09:53

When it comes to internal functions like that, I don’t fuss too much since the API’s… well, internal. So, going back to @smw’s example, that inner function would be fine for me as get-source or something. And then I’d make both maps transducers and string them together rather than nest them.

smw18:09:20

(let [keys-for-hit #(get-in % :_source :ecr-code)
      keys-for-object #(map keys-for-hit %)]

smw18:09:28

I was away for a bit, but I was trying to do it this way 🙂

smw18:09:44

but maybe a for is the right answer

smw18:09:58

for in clojure is basically a list comprehension, right?

akiva18:09:11

Honestly, as long as there isn’t a performance hit and it reads well, then go with what you like.

akiva18:09:43

I’ve discovered there’s almost always a more succinct way to do things but nothing wrong with a little verbosity if it makes the code easier to reason about.

akiva18:09:16

Not sure if letfn would be better here for this rather than defining functions in a let.

smw18:09:27

Yeah, I just don’t like polluting the namespace with one-off function defs that aren’t obviously related to the task at hand… even if they’re private.

smw18:09:34

oooh, I don’t know letfn

smw18:09:48

I also found clojure.walk while searching

akiva18:09:36

Actually, @smw, that’s one of the foundations of functional programming (those foundations being rather fractalized) is that it’s better to have 100 functions that each do one thing incredibly well than one bloated function that does many things poorly.

smw18:09:52

Sure, but if the functions are one-shot and not really composable, does it make sense to name them at the root of the namespace?

akiva18:09:37

Good question and something I sometimes struggle with. But I’ve no qualms wrapping a deeper namespace in a one-liner in a namespace closer to the API layer if it makes the code clearer. For instance, in one project, I have a bunch of entities that need to be saved to the database and instead of just always calling directly to the raw database save, they each have their own save function that literally just wraps the raw save. That way, I can have code like (entity/save entity-instance).

akiva18:09:02

And if later I need to do some customization before save, I won’t break calling code. And I’d rather compose off of that than an integration layer.

smw18:09:15

so each entity is in a separate file?

akiva18:09:42

Yep. Once you get comfortable with function proliferation, it becomes a matter of organization, really.

akiva18:09:27

I still use anonymous functions, mind you, when it’s something super-terse and easily understood.

akiva18:09:51

But I’d probably not nest a filter in a map, for instance.

alpheus19:09:09

Does Clojure have any notion of self-referential data structures, such as you might see in Lisp '#1=(1 2 3 . #1#)

Alex Miller (Clojure team)19:09:48

you can do it via atoms or other stateful things if you work at it, but: don’t :)

ghadi19:09:28

similar to 'tying the knot'. Reiterating it's not a good idea because clojure favors values rather than references.... but @bbloom has done it in fipp i think

ghadi19:09:40

christophe grand has some examples with promises somewhere

alpheus19:09:13

mostly just curious, but sometimes in maps such as the one in lein's project.clj, I've had to duplicate stuff in another key. Thought it might be nice if there was a way to refer to other parts of the same data structure.

bbloom19:09:18

@ghadi i don’t believe fipp does anything like that

ghadi19:09:45

not slot references per se

ghadi19:09:58

oh tying the knot was the haskell impl

richiardiandrea19:09:24

@akiva wow didn't know you had created that, it looks nice 😉

bbloom19:09:29

oh yeah - the “lazy” version vs the yield version

akiva19:09:44

Thanks, @richiardiandrea! I hope that wasn’t sarcasm. Hah.

richiardiandrea19:09:20

lol, no no I am checking the code 🙂 You also have Twitter in there, that I always wanted to have in a blog. When I was working for Lambda-X we used cryogen

akiva19:09:24

I’m the world’s worst at taking compliments.

akiva19:09:01

The twitter-api library is kind of old but it works… except with Clojure 1.9. It breaks spec all over the place.

richiardiandrea19:09:18

yeah I opened an issue there 🙂

akiva19:09:30

And cryogen’s great. I did my own version in JavaScript/Node.

richiardiandrea19:09:52

we will eventually need to fix it because my current project has that dependency

akiva19:09:44

That reminds me: I need to file an issue against 1.9’s new uuid function.

akiva19:09:21

This might just be me but as it is right now it throws if it encounters anything that isn’t already a java.util.UUID instance so it’s kind of useless when you’re pulling UUIDs as strings out of a database. So it isn’t a breaking issue, really, it just seems rather limited in comparison with how I’ve seen people handle and process UUIDs from different sources.

akiva19:09:09

To further the point a bit, if you have to convert a string to a java.util.UUID, you’ll never need uuid? anyway because you’ve already converted to a UUID instance.

seancorfield19:09:58

But if you’re passing data through multiple layers and you have a routine that expects a UUID then it’s useful.

akiva19:09:08

I agree. I just think it could be more useful.

seancorfield19:09:24

Also bear in mind that there are multiple string forms of UUID. And also multiple binary forms.

seancorfield19:09:42

I think we have to deal with four different UUID representations at work.

akiva19:09:09

We’re mainly dealing with java.util.UUID with uuid? so the other representations aren’t as relevant and would require handling on the dev end. I’m fine with it staying in line with java.util.UUID but being able to recognize a string that adheres to the JVM’s UUID as something that could be converted correctly to an instance would be helpful.

akiva19:09:21

At least for me, anyway. Heh. I just wrote my own version.

akiva19:09:52

This actually relates with @lvh having an issue with prone. I forgot about that.

Alex Miller (Clojure team)19:09:58

how could it check whether a string is a uuid?

alpheus19:09:47

is this a setup for the old "a programmer has a problem and thinks I'll solve it with a regex" joke/

akiva19:09:11

Eh, I just needed it to work so I wrapped (. java.util.UUID fromString s) in a try.

akiva19:09:30

@alpheus, hah, believe it or not, not this time.

Alex Miller (Clojure team)19:09:53

I suppose, not sure if Rich would go for that or not

akiva19:09:18

And honestly, I’ve been hesitant because I simply wasn’t sure how halo this case is.

akiva19:09:30

I just know that a lot of people deal with UUID strings a lot.

bfabry19:09:59

I think it'd be surprising if uuid? returned true for both a string and a UUID. would it also need to generate both strings and UUID objects?

akiva19:09:23

uuid? doesn’t generate objects. It just says, ‘Yes, this is a java.util.UUID.'

bfabry19:09:45

sorry I mean would the associated generator also have to generate both uuid strings and uuid objects

akiva19:09:45

Or, in my use case, a string that could be one.

sveri19:09:20

Well if uuid? returned true for a string I would consider this a bug

akiva19:09:22

@bfabry, possibly. I’m coding against MongoDB so I’m using its random-uuid.

seancorfield19:09:46

(s/exercise uuid?) does indeed produce UUID objects @akiva

seancorfield19:09:14

And you could write a generator for UUID strings based on that (use uuid? to generate the UUID and then fmap it to a string of the appropriate form.

seancorfield19:09:25

boot.user=> (s/exercise uuid?)
([#uuid "d46604d9-43be-415b-96fd-616992227e0b" #uuid "d46604d9-43be-415b-96fd-616992227e0b"] [#uuid "219f702b-59f3-4165-ac7f-2ffb76a06e73" #uuid "219f702b-59f3-4165-ac7f-2ffb76a06e73"] [#uuid "fe832237-2f88-4621-a912-45c45959d3b1" #uuid "fe832237-2f88-4621-a912-45c45959d3b1"] [#uuid "10e03132-a9da-4b4b-9880-b497310967a4" #uuid "10e03132-a9da-4b4b-9880-b497310967a4"] [#uuid "cbcd32a3-7e21-452a-9505-59d36de3c2fd" #uuid "cbcd32a3-7e21-452a-9505-59d36de3c2fd"] [#uuid "50d2dce5-2427-4304-9a17-e259f3db78bf" #uuid "50d2dce5-2427-4304-9a17-e259f3db78bf"] [#uuid "3fc3768a-68ec-46da-ba1e-15b7e91063bc" #uuid "3fc3768a-68ec-46da-ba1e-15b7e91063bc"] [#uuid "b992038c-f4dc-41fe-8349-41849fa90c36" #uuid "b992038c-f4dc-41fe-8349-41849fa90c36"] [#uuid "5d1f1321-9bba-4806-9a86-88cc3977047a" #uuid "5d1f1321-9bba-4806-9a86-88cc3977047a"] [#uuid "8f4327b4-6869-4c61-ae94-cc7d8e1822f8" #uuid "8f4327b4-6869-4c61-ae94-cc7d8e1822f8"])

seancorfield19:09:14

(just showing that uuid? can generate objects)

bja19:09:53

is there a way to tell if an object can be treated as a value in clojure?

akiva19:09:05

I have to admit, I’m spec-ignorant at this point. I’m just looking at the code and it shows it returning a boolean. I need to read up on this some more. It’s also why I haven’t filed an issue. I’m not about to bust through a wall like the Kool-Aid man on clojure.core. Heh.

akiva19:09:57

Bringing this back home to my point with @smw, though, this is a good example of how uuid? simply wraps a one-liner around deeper code.

akiva19:09:52

Interesting, @bfabry. It’s showing uuid? mapping to (uuid) which, I imagine, would produce UUID instances.

akiva19:09:56

The uuid? function merely tests whether the input is (instance? java.util.UUID x).

smw19:09:40

Thanks, akiva. Good point.

akiva19:09:42

But I also like @sveri’s comment that a string representation of a UUID isn’t actually a UUID even if it could be used as input to creating a proper java.util.UUID.

akiva19:09:06

I think my issue is solved with (class #uuid "550e8400-e29b-41d4-a716-446655440000”). Wrapping the string in the (class #uuid) and passing that to uuid?. Boom.

akiva19:09:00

Heh, basically. What could be used to create a UUID isn’t yet a UUID and thus would fail uuid? because it’s not a UUID yet.

akiva19:09:40

Glad I brought it up. I learned a lot. Thanks, everybody!

ag23:09:30

is it ok when clojure.tools.reader.edn/read-string replaces clojure.core/read-string?

hiredman23:09:02

clojure.tools.reader.edn/read-string is more like clojure.edn/read-string, but all the read-strings have a large overlap

ag23:09:09

sorry, I meant clojure.edn/read-string