Fork me on GitHub
#clojure
<
2017-12-08
>
kgofhedgehogs00:12:11

Uh. Switching to HoneySQL. Can't find how add RETURNING into query builder. Can anyone help?

sandbags11:12:05

To scan a whole file system (collecting information about file sizes), is recursively applying .listFiles the fastest option available to me? I couldn't immediately work out whether it was based on nio2 which seems to be the lowest level option available in Java? Anecdotally my approach seems orders of magnitude slower than, for example, DaisyDisk (I am on a Mac) which appears (although maybe they have some way of deferring the fetching of individual file information for a folder?) to be able to gather the same information in under 30s. -- digging around in the Java I see java.nio.file.Files#walkFileTree and I don't think .listFiles is using that so I guess I should start digging there.

noisesmith18:12:22

for recursively looking at the fs, check out file-seq - it returns a lazy seq of files

noisesmith18:12:14

also I would assume DaisyDisk is exploiting your mac’s spotlight cache, or you have a very small file system and the clojure approach was extremely inefficient

sandbags10:12:30

Thanks @noisesmith, I did start with file-seq but noticed it was based on .listFiles and since the laziness didn't seem to be an advantage to me used that directly. I've also tried an nio based approach. There's 458GB of files in on my file system and DaisyDisk is scanning that in ~13s, my approach doesn't complete in less than 15 mins (at which point I abort it).

sandbags10:12:25

I'm sure DD must be using something but cannot yet see how it might be the spotlight cache since my researches so far suggest that it doesn't contain a complete list of files on the file-system

fabrao12:12:43

Hello all, how do I convert this?

SSHClient ssh = new SSHClient(); 
        ssh.addHostKeyVerifier( 
                new HostKeyVerifier() { 
                    @Override 
                    public boolean verify(String s, int i, PublicKey publicKey) { 
                        return true; 
                    } 
                }); 

vemv12:12:40

@fabrao reify for new HostKeyVerifier() { which seems the only non-straightforward part

Bravi12:12:34

how can I do traverse in clojure? So I have an array of [1 2 3 4] and I’d like to call let’s say get-user function, which accepts a user ID and does an http call and then I want to end up with an array of results basically

Bravi12:12:06

if I use map, will that hold the computation that happens afterwards, before everything is resolved?

fmind12:12:34

@bravilogy yes, as long as you realize the computation

fmind12:12:09

and use doall to ensure you produce the side effect https://clojuredocs.org/clojure.core/doall

Bravi12:12:34

gotcha, thank you!

Bravi12:12:23

that’s cool 😄

sandbags12:12:35

Hrmm... even using hara.io.file.list which seems to be nio based is glacially slow. I guess apps like DaisyDisk must be using OSX specific apis not available to Java.

mbjarland12:12:10

trying to wrap my head around fully qualified keywords, would the following be a valid way to refer to a spec in another namespace:

(ns foo
  (:require [clojure.spec :as s]))
(s/def ::layout-string ...)

(ns bar
  (:require [clojure.spec :as s]))

(s/fdef parse-layout-string
        :args :foo/layout-string
        ...)
? (I ask since I’m getting an interesting ‘Call to clojure.core/ns did not conform to spec’ error)

fmind13:12:06

yes, "::" resolves to your current namespace

fmind13:12:40

but you should put (:require foo) in ns bar

mbjarland13:12:42

@fmind seems my error was an incompatibility in my dependency chain with clojure 1.9…could be I’m a bit thick here, but I don’t always find those spec error messages entirely easy to grok

Empperi13:12:00

they certainly take some getting used to

fmind13:12:54

you can try to make them more readable with https://github.com/bhb/expound

mbjarland13:12:18

@niklas.collin @fmind thanks for the pointer, I’ll take a look at expound

mbjarland13:12:40

problem is this was the clojure compiler complaining about code I didn’t write. Not sure it would be possible to force the clojure internal spec conform calls to use expound…

Empperi13:12:08

if that is set then spec uses expound to do it’s error printing

mbjarland13:12:33

would it be possible to inject this early enough in the build process so that when clojure checks my transitive dependencies, it would use expound/printer? (I’m using leiningen)

Empperi13:12:51

not sure, haven’t tried it. But you can execute arbitrary code within Leiningen. The problem is though, that if the exception actually does truly happen during dependency resolution then it is kinda too early since spec itself is a dependency

Empperi13:12:57

and so is expound

Empperi13:12:23

I would be super surprised the problem would surface during dependency resolution though

Empperi13:12:02

after dependencies you can execute code via leiningen :injections, see example here https://github.com/technomancy/leiningen/blob/master/sample.project.clj#L182

Alex Miller (Clojure team)13:12:08

You could put it in user.clj - thats loaded pretty early

Empperi13:12:18

user.clj is another option, that comes later than :injections but if that is enough then it would be preferrable.

mbjarland13:12:19

I think one of my dependencies was not built to work with clojure 1.9 and fails a few checks when it is, I have solved the issue by upping the dependency to a version which is built for 1.9 so this is somewhat academic for the purposes of my immediate issue, but interesting nevertheless

Empperi13:12:43

right, well that would not break during dependency resolution

mbjarland13:12:44

thank you both for the pointers

Empperi13:12:49

but more when it gets loaded

Empperi13:12:09

so, you have plenty of time to hook expound as spec error printer

mbjarland13:12:10

and here my clojure newbieness shines through a bit. Would not the dependencies be loaded on “first refer” or something, i.e. probably before the code in core.clj etc is executed?

Empperi13:12:43

if your code is not AOT compiled then dependencies etc are loaded when you require them

Empperi13:12:45

so, if you add the expound hook into user.clj - which is the very first thing Clojure loads - and there only refer spec and expound then you will get that code executed before another piece of code gets executed which will try to require the offending library

mbjarland13:12:46

which would normally be in the ns block at the top, so the error printer hooking would have to happen before that at least

mbjarland13:12:41

@niklas.collin ok, thank you again!

Empperi13:12:16

np, the dynamic nature of Clojure is really powerful, it has a slight drawback of relatively high startup time but in general it’s benefits far outweigh the downsides

Empperi13:12:47

you pretty much cannot do stuff like that in most of the languages

wotbrew14:12:17

It is ok to seek feedback for a library idea here? Its a rather big idea so before releasing anything I and investing too much I want to make sure its not totally wrong! edit: https://www.reddit.com/r/Clojure/comments/7if66b/objection_another_component_alternative_is_this_a/ gh: https://github.com/riverford/objection

wotbrew14:12:32

I've started a thread.

wotbrew14:12:24

@niklas.collin could you paste your stuff here? 🙂

Empperi14:12:50

Empperi [4:17 PM] ok, I read the github page [4:18] really doesn’t look that different from mount to me [4:18] for example, the “lazy construction instead of explicit single start! fn.” is not strictly totally true, you can specify which components you want started with mount when you call it’s start! [4:20] so, I would say that would look great if there were no mount available, since it is however I do not see any real value I would get out of that

Empperi14:12:23

so, I wouldn’t say “don’t do it” but as it is now it doesn’t look compelling enough to me to justify a new library

Empperi14:12:37

I would prefer seeing some PRs in mount to improve it if you find it lacking

Empperi14:12:49

but, I got to go and cannot give you any more relevant feedback. Do not kill your idea based on just what I said 🙂

wotbrew14:12:55

I do not use mount, I have looked at it in the past - in fact a lot of this came out of a lack of satisfaction with mount/integrant/component for certain kinds of system. I do appreciate 'yet another component/mount/bla' alternative, and one of the main reasons why I am apprehensive. I'll adjust the readme a bit to try and help demonstrate why its different, and where the value prop is of this over the others. Thanks for taking the time to give it a look 🙂

wotbrew15:12:38

For any that are interested I put a bit more in the readme to attempt to explain things a bit better

Empperi14:12:14

why wouldn’t it be? 🙂

Empperi14:12:28

tell us about your idea and we’ll evaluate it

Empperi14:12:34

I might not have time right now but someone will

wotbrew14:12:30

I've attached a reddit thread/github link 🙂

wotbrew14:12:56

Any comments would be greatly appreciated, just so I know whether to invest in it - I have lots of open-source work that I want to do and want to get a feel for whether its worth the risk on this one (as it seems very risky)

Empperi14:12:40

ok, I read the github page

Empperi14:12:14

really doesn’t look that different from mount to me

Empperi14:12:45

for example, the “lazy construction instead of explicit single start! fn.” is not strictly totally true, you can specify which components you want started with mount when you call it’s start!

Empperi14:12:09

so, I would say that would look great if there were no mount available, since it is however I do not see any real value I would get out of that

tbaldridge18:12:42

@U0GE2S1NH (pinging him here to add him to this thread)

tbaldridge18:12:12

The big difference vs mount is that this doesn't use global singletons, which is what I absolutely can't stand about mount.

tbaldridge18:12:51

It looks like objection is a global (singleton) registry of any number of components.

tbaldridge18:12:56

So if you need more than one DB connection you can simply start up and register multiple connections. All components seem to have their own UUIDs keeps components from getting lost, as well as allows for multiple instances of the same component.

tbaldridge18:12:22

@U0GE2S1NH I don't not like it, and the more I think about it the more I like it. 🙂

wotbrew18:12:39

Singletons aren't always bad (e.g queue and threadpool used as some internal optimization, no effect on api, want flexibility to change locally) - so I do support them.

wotbrew18:12:11

Not sure about using them for all state though, so I wanted to make the dynamic/open case the primary one.

wotbrew18:12:36

I've always been a component/integrant user too, not really used mount - though I tried it a couple of times

bronsa14:12:37

looks like clojure 1.9 is out :)

souenzzo14:12:30

while [[ "$(curl -s -o/dev/null '' -D - | awk '/^HTTP/{ print $2 }')" -ne "200" ]]; do sleep 2; done; echo 🎉;

bronsa14:12:50

doesn't seem like it's in maven central yet, but it should be soon

eggsyntax15:12:53

Congrats to the core team -- nice work, y'all 👏

New To Clojure15:12:22

But http://clojure.org still has nothing about release

bronsa15:12:47

give the core team some time :)

mbjarland15:12:38

$> lein test
Retrieving org/clojure/clojure/1.9.0/clojure-1.9.0.pom from central
Retrieving org/clojure/clojure/1.9.0/clojure-1.9.0.jar from central
: )

Drew Verlee18:12:48

I have been doing some brain storming on how to make more understandable software. I’m trying to turn those ideas into something actionable, maybe a clojure library. The current idea is to give projects more of a narrative. I wrote up a quick proposal of what this might look like here: https://github.com/drewverlee/narrative/blob/master/ideas.md I welcome any feedback. Thanks

noisesmith19:12:39

I think good tests read like a commentary track dvd extra for the code - so it should have at least that level of narrative

noisesmith19:12:56

there’s some interesting ideas here, thanks

Drew Verlee19:12:10

Thanks noise, I think good tests can do that too. But that still wouldn't be the same as telling a story about the project ... Or do you disagree? I think this kind of narrative usually gets put in the readme. It also just occurred to me that I should be clear the narrative I had in mind is aimed at developing on the project not being a end user... Though you could probably use the same mental framework too construct one towards that end as well.

luskwater15:12:45

Since you mention “developing”, it seems to me that you want to communicate the (eventually shared) mental model of the project. I remember reading an article on the need for this, and how different mental models over time cause problems or technical debt, but can’t find it.

qqq19:12:38

(merge nil {:a 2})
(comment
 {:a 2})

(merge (persistent! (transient nil)) {:a 2})
;; ==> exception


is this because transient can't figure out if nil should become {} () [] or #{} ?

bronsa19:12:40

you can't make a transient out of nil

tbaldridge19:12:10

What you want is (transient (empty {:a 2})) or something like that

bronsa19:12:25

well, thinking that it's because "transient can't figure out if nil should become [] () [] or #{}" is a bit of a mis-statement

tbaldridge19:12:21

Right, "can't create transient from null" is the technical reason. "can't figure out if..." is the reason that it's not fixed

bronsa19:12:14

taking this to a thread as this is going to be a bit nit-picky -- the reason why I wouldn't reply "yes" to that question is that often people that don't really understand how nil is treated in the clojure stlib think that e.g (merge nil {1 2}) works is because they think that nil can "act" as an empty collection of all types, when in reality that's only true in the case of seqs, and accepting that interpretation could be a bit confusing

bronsa19:12:12

when the reality is that merge and other functions explicitely filter out nils or replace them with the appropriate empty coll, not that they know how to "interpret nil" as the correct empty coll

qqq19:12:59

@bronsa: I see, thanks for the clarification. This is precisely the mental model mistakes I was making.

qqq19:12:28

the context is that I wrote this function:

(defn merge-drop-vEmpty [a b]
  (persistent! 
   (reduce (fn [old [k v]]
             (if (or (= v nil) (= v {})) 
               (dissoc! old k)
               (assoc! old k v)))
           (transient a) b)))

qqq19:12:56

and it's a bit weird, since (merge nil {...}) is fine but (merge-drop-vEmpty nil {...}) is bad

tbaldridge19:12:15

Well you can do the same, you just need to give it a default if a is nil

tbaldridge19:12:22

(transient (or a {}))

noisesmith20:12:39

@qqq another possible version (not so sure it’s an improvement but it lets me demo transduce)

(defn merge-drop-vEmpty
  [a b]
  (transduce
   identity
   (fn
     ([] (transient (or a {})))
     ([v] (persistent! v))
     ([old [k v]]
      (if (contains? #{nil {}} v)
        (dissoc! old k)
        (assoc! old k v))))
   b))

noisesmith20:12:59

I do like the fact that it has a completing arity instead of needing the completion function to wrap the call

noisesmith20:12:23

but using identity as an xform makes me think this might be silly

noisesmith20:12:20

oh wait we could skip a bunch of BS by just using into here, because into already does the transient/persistent! thing for you

noisesmith20:12:50

but you can’t edit the first arg to into

mikerod21:12:25

I was actually never sure if (merge nil {:a 1}) working was reliably supported behavior

mikerod21:12:31

The doc string does not indicate that it should work

mikerod21:12:47

It is fairly annoying to not utilize it though at times

mikerod21:12:07

clojure.core/merge
([& maps])
  Returns a map that consists of the rest of the maps conj-ed onto
  the first.  If a key occurs in more than one map, the mapping from
  the latter (left-to-right) will be the mapping in the result.

bfabry21:12:17

that description seems to imply it shouldn't in fact

bfabry21:12:26

because

(conj nil {:a 1})
=> ({:a 1})
(merge nil {:a 1})
=> {:a 1}

ghadi21:12:55

it is supported, but yeah it makes somethings difficult

ghadi21:12:23

merge uses conj internally, but protects against nil

mikerod21:12:31

Just because a fn happens to do some behavior with nil now, doesn’t give me a lot of confidence that I’m not relying on an impl detail

mikerod21:12:47

I wish it’d be in the docs for these sorts of edge cases

mikerod21:12:29

There are plenty of cases like this though. The merge one is just one I’ve wrestled with in my head a few times.

mikerod21:12:56

Also, I’m thinking it’d be a pretty harsh change to have merge stop supporting this behavior - so not likely to change anymore 😛

ghadi21:12:58

core specs will help with this, can almost guarantee it won't go in the docstring

ghadi21:12:34

merge supports a couple other surprises, see the last comment in https://dev.clojure.org/jira/browse/CLJ-1458

bfabry21:12:08

I feel like if a spec is added to merge that says (s/nilable m) then a subsequent docstring PR that clarified what happened in the case of nil would prob be accepted

mikerod21:12:05

@ghadi good point on the core.specs. I just took a look over at that (haven’t looked in a while). It still has a lot more functions to cover still right? At least from what I see so far.

ghadi21:12:17

It's mostly the main macros right now, @alexmiller has more stuff pending somewhere, and there are a couple of github users that have their own specs of core

ghadi21:12:22

generally clojure docstrings don't comprehensively specify edge-cases, but http://clojuredocs.org or whatever the thing-du-jour is certainly can

ghadi22:12:35

"Someone should" add an API to third-party clojuredocs and integrate them with tooling

mikerod22:12:28

I’m typically ok with that idea

mikerod22:12:18

Then other times I have a hard time deciding whether I’m relying on an odd impl detail or not

mikerod22:12:45

Interesting Jira though, read through the CLJ-1458 one

andy.fingerhut22:12:39

The stricter interpretation you take of the doc strings, the less likely you are to be relying on an odd impl detail.

andy.fingerhut22:12:53

I know that is still pretty vague advice, but at least in the case of merge, if you never pass it nil, you are safe. If you do, maybe it could return anything at all or raise an exception in Clojure 1.13

andy.fingerhut22:12:40

Not saying it is likely, given the backwards compatibility effort Rich et al put into Clojure.

mikerod22:12:56

@andy.fingerhut yeah, and I’ve found myself a few times being defensive about it out of that sort of worry

(update x merge {:a 1}) ; not this
(update x (fnil merge {}) {:a 1}) ; this

mikerod22:12:58

that sort of thing

mikerod22:12:16

I then just start to wonder if it is worth the nil-guarding efforts

ghadi22:12:25

and (generally) the more the docstring promises, the narrower a future path might be

mikerod22:12:07

the trade-off, make more verbose code to not rely on things vs hope for the best, but be prepared for potentially more difficult upgrades in the future

mikerod22:12:29

and perhaps watch the Jiras carefully and fight for whatever obscure use-case you see that someone is about to break 😛

mikerod22:12:49

I’m not saying what is right vs wrong here. Just explaining my internal conflict and struggle

noisesmith22:12:02

@mikerod total aside: that update could just use assoc

=> (assoc nil :a 0)
{:a 0}

mikerod22:12:17

@noisesmith hah thanks, I didn’t intend to make something realistic

noisesmith22:12:56

it’s one of my pet clojure style issues, I just had to say something, I compulsively replace calls to merge that use a map literal as a second arg with calls to assoc (or instruct co-workers to do so when doing code reviews)

noisesmith22:12:27

that’s what makes it an aside haha

mikerod22:12:27

wasn’t a great example 😛

ghadi22:12:33

(and nil in merge is only problematic in the first argument)

noisesmith22:12:58

oooh (fnil merge {} {}) is also valid though

ghadi22:12:47

What is a spec for merge?

ghadi22:12:01

at least the :args part

ghadi22:12:18

and ignoring the GIGO cases

noprompt22:12:44

seems like if-not could just ditch the expansion with not i.e.

(defmacro if-not
  ([test then] `(if ~test nil ~then))
  ([test then else] `(if ~test ~else ~then)))

noprompt22:12:09

right, it’s just if with the args flipped.

noprompt22:12:23

minor thing.

bronsa22:12:06

shouldn't m0 be nilable?

ghadi22:12:14

nope, that's the GIGO

noprompt22:12:30

what is GIGO?

ghadi22:12:36

garbage in garbage out

bronsa22:12:41

ah, I thought you were speccing the current behaviour :)

noprompt22:12:09

GIGO is such a punitive perspective.

ghadi22:12:42

Ah, you are right @bronsa

bronsa22:12:18

am I? and to think you just convinced me that you were right..

ghadi22:12:48

i am definitely wrong, it's 530PM EST on friday.

bronsa22:12:21

TIL (merge) works

arrdem22:12:26

go home ghadi

ghadi22:12:33

(I think I confused myself with the metadata propagation, which only happens when the first arg is not nil)

ghadi22:12:05

But :m0 should be (s/nilable (s/keys))

noisesmith22:12:12

@bronsa the best parge is that (merge) returns nil

ghadi22:12:18

haha yes ^

bronsa22:12:31

I have a feeling that part is definitely gigo

ghadi22:12:32

it is a strange implementation

ghadi22:12:45

the some identity check at the beginning

bronsa22:12:47

and it's not really supposed to work

bronsa22:12:57

¯\(ツ)

noprompt22:12:19

can someone explain to me what is attractive about the GIGO rationale?

bronsa22:12:27

I'll just write nil as (merge nil nil nil nil nil) from now on

noprompt22:12:28

i’ve always found it punitive.

bfabry22:12:59

there's nothing particularly attractive about it, it's just reality in a dynamic langauge. spec is moving us towards Garbage In EXCEPTION

noisesmith22:12:09

@bronsa merge -> (merge nil nil nil nil) get -> (get get get get)

ghadi22:12:13

it can be @noprompt... I think spec will really help with that.

ghadi22:12:35

user=> (merge [] {})
[{}]

bronsa22:12:46

no matter how many times I see that get over the years it always makes me chuckle

noisesmith22:12:07

it’s probably one of my greatest discoveries

bronsa22:12:17

one of many

noprompt22:12:30

@bfabry can you clarify your claim that it’s a “reality in dynamic languages”. it’s not clear to me how typing discipline plays a role here.

noprompt22:12:47

@ghadi yes, that is one thing i find attractive about it (spec).

bronsa22:12:55

somebody should work on a dialect of swearjure that only uses weird stuff like this

noisesmith22:12:13

for those who are unfamiliar

=> (= get ((get get get get) {get get} get))
true

bfabry22:12:59

well, how would you prevent gigo in clojure? spec! what happens if you don't do something like that? gigo! ie, gigo is just the default case in a dynamic language where data is passed around in structures with uniform interfaces

noprompt22:12:49

@bfabry that may be true from some cases but that has more to do with the programmer who wrote the source. it’s not inherently a problem in dynamic languages.

bfabry22:12:15

another option would be to sanity check all inputs manually. I'm glad no one took the time to do that seeing as spec exists now and that code would just be noise

noprompt22:12:58

@bfabry i disagree. :pre and :post checks, which i use heavily, are useful.

ghadi22:12:09

but they're super noisy

noprompt22:12:19

for development.

noprompt22:12:41

and only when *check-asserts* is true. instrumentation with spec is extremely slow as well.

bfabry22:12:53

they're certainly useful. but they're noisy, and quite a lot of work. I think spec is a much more elegant solution because it gives you some sort of structural documentation and errors as well as validation

bfabry22:12:46

if someone had gone through core and :pre'd every function then the current work of going through core and spec'ing every function would feel a bit redundant

noprompt22:12:53

so basically, the argument in favor of GIGO is “i don’t want to write the sanity checks for my code because it’s noisy”?

arrdem22:12:00

pretty much

bfabry22:12:18

"I don't want to write the sanity checks because it's noisy and I have more important things to write"

noprompt22:12:23

that seems like a weak argument against clearly expressing your domain, range, semantics, etc.

bfabry22:12:32

and that's why spec has been created?

noisesmith22:12:44

not just that - overly strict preconditions can make later extensions tedious or incompatible

noprompt22:12:09

@noisesmith i can see that, although, i’ve never come up against that in practice.

noisesmith22:12:58

@noprompt simple example - imagine whitelisting the allowed keys in a hash-map - you now need to rewrite your code as soon as code around it is extended, even though it shouldn’t have to care

noisesmith22:12:12

other cases are more subtle but often just as tedious

noprompt22:12:19

@bfabry i’ve seen that attitude fail catastrophically though, enough so that :post (some? %) has saved me hours of debugging.

seancorfield23:12:14

(defn foo [x] {:post (some? %)} x)
may look like it works but it actually asserts some? (truthy) and then asserts % (the value returned from foo) so it needs to be
(defn foo [x] {:post [(some? %)]} x)
although the following actually works too -- but looks odd:
(defn foo [x] {:post (%)} x)
(`[%]` works the same as (%) here -- it's just a sequence of forms to evaluate with % bound to the function result. This was a surprise to me when I tried it in the REPL!

noisesmith22:12:38

you can get future proof code without a bunch of complex incantations by failing to validate, once you validate future proofing is more work, and adds complexity in which bugs can hide

noisesmith22:12:56

(unless the validating comes from the outside, eg. spec)

bfabry22:12:12

I don't think anyone disagrees that some validation can be very useful. which is presumably why schema became so popular and why spec was created. I personally think on balance :pre and :post don't give enough extra value to go along with the validation to make them worth it the vast majority of the time. schema gave a bit more value and now spec gives even more to help tip the scales towards making it worth doing

noprompt22:12:17

@noisesmith i suppose my argument there would that, to your point, the constraint is too tight. spec doesn’t alleviate you from that either.

arrdem22:12:49

if only there were technologies for late-parameterizing specifications with additional requirements

noprompt22:12:29

so GIGO is not salient when constraints are and vice-versa?

bronsa22:12:36

I don't understand what we're arguing about, clojure had a GIGO problem and now we have spec to avoid that

andy.fingerhut22:12:37

I thought GIGO in Clojure was purely motivated by run-time efficiency ?

bronsa22:12:05

I wouldn't say purely but that's the biggest reason

noprompt22:12:07

@bronsa i was simply trying to understand the attraction to the GIGO rationale.

andy.fingerhut22:12:16

And have any core Clojure developers ever called it GIGO, or is that something originated by others?

bfabry22:12:19

GIGO isn't a philosophy or a rationale, it's just a statement. if you pass nonsense in that there's no validation checks around you can expect to get nonsense out

bronsa22:12:38

@andy.fingerhut I'm sure I've seen alex call it GIGO more than once :)

noprompt22:12:56

i’ve seen that too.

noprompt22:12:15

@bfabry in some cases it is a rationale.

bfabry22:12:25

I would like to see one of those cases

noisesmith22:12:33

while GIGO is just a fact, there’s a rationale to prefering code with GIGO behavior over defensive code - which does describe clojure code before spec

bfabry22:12:17

yeah but that preference is because of "performance" or "cleaner code" or "whatever". it's not like anyone is specifically trying to write code to silently fail

bronsa22:12:03

meh, you make it sound like most GIGO in clojure was a design decision when in reality it is just lack of validation to avoid: - runtime performance costs - having to manually write explicit checks for everything

andy.fingerhut22:12:13

@bfabry If you mean an example of GIGO being a rationale to avoid adding run-time type checks, the clojure.set operators are the biggest example in my mind, e.g. https://dev.clojure.org/jira/browse/CLJ-1953

bronsa22:12:21

@noprompt ghadi isn't a core developer

noprompt22:12:39

@bronsa what does that have to do with the discussion?

bronsa22:12:50

that's what andy asked

bfabry22:12:59

they're using "gigo" to describe "that's a case where the function does something unintended because you passed in something unintended". I don't see anyone saying "it should be that way cuz gigo"

noprompt22:12:01

i was merely interested in why GIGO is floated as an answer ever.

bfabry22:12:33

because it's a succinct and accurate one?

bronsa22:12:34

look, s/GIGO/undefined behaviour/

bronsa22:12:55

gigo is just shorter to type

bfabry22:12:39

@andy.fingerhut no one is saying they're not validating clojure.set "because gigo". If you were to specifically ask my guess would be "because performance and can't be bothered"

noprompt22:12:00

@bfabry i’m pretty sure that’s been floated as a rationale in that discussion.

bronsa22:12:37

core members have said in multiple forums that spec will "solve" the gigo issues of clojure.set

bronsa22:12:46

so clearly gigo is not a rationale

bfabry22:12:54

in one of the linked tickets "Now that set is faster for sets, I think we could actually add checking for sets in some places where we might not have before. "

bfabry22:12:11

so the previous reason was performance. and the current reason is "better things to do so far"

bfabry22:12:35

also, lucky they did those better things, because they made spec! and now that validation is trivially added in a uniform way rather than ad-hoc

noprompt22:12:44

@bronsa my line of question was in general not with respect to only core members.

noprompt22:12:18

it’s was more of “i’ve seen this GIGO thing be used as a rationale in the context of the clojure community and i’m curious what motivates it.”

bronsa22:12:41

right, I was assuming we were specifically talking about core

andy.fingerhut22:12:40

@noprompt The community cannot change clojure.core without the approval of one person. If someone else gives a rationale for what changes in clojure.core, or does not change in clojure.core, it is based on their interpretation/guesstimate of the reasons.

Alex Miller (Clojure team)22:12:26

hey, someone write specs for the clojure.set functions and file a patch

bronsa22:12:44

there you go :)

andy.fingerhut22:12:52

My interpretation is that "can't be bothered to add the checks" seems unlikely for things like clojure.set functions, since many people would have been happy to provide patches for those years ago if they were desired by the core developer team. spec being a far more general tool is fantastic, and I'm glad it was created.

Alex Miller (Clojure team)22:12:59

that’d be like, useful

Alex Miller (Clojure team)22:12:10

you don’t even have to wait for them to be in core to use them

noprompt22:12:16

@andy.fingerhut i understand that, however, my question was more broad in scope. i’m not calling out clojure core, the libraries, or the members. it was a question for the room in the context of the community purely because i’ve seen it be a rationale for doing something not bound by the performance reason.

Alex Miller (Clojure team)22:12:20

just put ’em in a namespace and load them

noprompt22:12:13

the typing thing, well, that’s effectively a fallacy (in my opinion).

noprompt22:12:45

to say “i won’t do x because typing” can be a fine argument in certain situations but in general it’s very weak.

bfabry23:12:59

the point was simply that you see that kind of beahaviour less in typed languages (at an unacceptable cost). again. not a rationale. a statement

Alex Miller (Clojure team)23:12:00

in my experience, writing specs for core stuff can bring out many subtle questions. going through those and teasing apart what is expected, what works but is unexpected, and what doesn’t work now but we might want to work in the future has some measure of art to it.

noprompt23:12:04

the way i read that argument is “my program won’t/will have this set of properties because (not) typing”.

bronsa23:12:48

I don't understand what "typing" you mean here :) the fingers on a keyboard one or the holy war one

noprompt23:12:07

@bronsa fingers on the board.

noprompt23:12:56

i don’t believe in arguing about static vs dynamic typing. it’s a pointless endeavor. folks should be open to good ideas period and stop obsessing about typing discipline.

borkdude23:12:01

congrats on clojure 1.9.0!

noprompt23:12:34

i’m actually filled with a bit of dread now because it means i have to finish garden 2.0.0. 😂

Alex Miller (Clojure team)23:12:43

I’m celebrating with some Templeton Rye 6 year myself :)

the2bears23:12:54

Your rye should be older than your programming language 🙂

borkdude23:12:13

I celebrated by upgrading our dev branch and pushed to staging

Alex Miller (Clojure team)23:12:37

I have some scotch that would qualify, but I keep coming back to this Templeton lately

arrdem23:12:07

3pm PST, I’m in for 1.9.0 scotch 😄

arrdem23:12:27

Congratulations Alex

andy.fingerhut23:12:42

"Ahhh, the 1.9.0 --- that was a good version for Scotch..."

Alex Miller (Clojure team)23:12:57

they really don’t know how to do version numbers well in scotch

Alex Miller (Clojure team)23:12:09

branching strategies are crazy

Alex Miller (Clojure team)23:12:31

it’s like they were all drunk or something

borkdude23:12:59

@alexmiller Out of curiosity, was it just waiting for bug reports or actually still fixing stuff in the last few weeks?

Alex Miller (Clojure team)23:12:21

well the last few weeks has mostly been me rewriting the docs over and over at Rich’s direction :)

andy.fingerhut23:12:43

Gotta make sure the docs don't over-promise 🙂

Alex Miller (Clojure team)23:12:46

but also giving it some soak time in case anything came up

andy.fingerhut23:12:56

Sorry, I couldn't resist. Bad me[

borkdude23:12:13

@andy.fingerhut as long as the specs are valid 😉

Alex Miller (Clojure team)23:12:21

may all your specs be valid

Alex Miller (Clojure team)23:12:33

scottish blessing I think

noprompt23:12:15

thumbsup_all to the CLI stuff.

tvalerio23:12:04

Hi! Can you guys give me an idea of how to try to resolve this? I have a test that calls a functions. This functions executes an async task. Locally if I use a (Thread/sleep 4000) I can assert my data just fine. But, after I build my application on travis, the build stop in this test and never finalize the build. Someone hava already seen this?

noprompt23:12:03

@tvalerio are you able to patch your test (or code) in such a way that it does not require the task to be executed asynchronously? i know that’s not answering your question, however, it’s one i would ask myself in that situation.

tvalerio23:12:37

@noprompt I don’t see a way to do that. I have to call this async function and wait for the entire process to finish. Otherwise, when I go to the database the data is not there yet

tvalerio23:12:00

(Thread/sleep) is the only way I thought how to do it

noprompt23:12:31

@tvalerio you could use a promise or a future.

noprompt23:12:08

@tvalerio actually i think a promise sounds like it might do the trick. you could create a promise, then execute your async task and, upon completion, deliver a value to the promise. at the end of the test you could simply dereference the promise as to block the thread and keep the build from exiting.

noprompt23:12:48

(let [p (promise )]
  (do-async-stuff
   ,,,
   (deliver p some-val))
  (deref p))

noprompt23:12:57

someone else might have a more elegant solution.

tvalerio23:12:11

but if use promise i’ll have to change the async task to put a value in the promisse right?

noprompt23:12:34

sounds like that’s the place in the code you need to patch.

noprompt23:12:31

so, yes. you need to be able to deliver to the promise when the async task completes. i’m guessing there’s probably a function or something that gets handed to whatever does the async work.

noprompt23:12:21

i hope i’m helping… 😅

tvalerio23:12:38

I see.. thanks for the help! I’ll try to fix this

seancorfield23:12:14

(defn foo [x] {:post (some? %)} x)
may look like it works but it actually asserts some? (truthy) and then asserts % (the value returned from foo) so it needs to be
(defn foo [x] {:post [(some? %)]} x)
although the following actually works too -- but looks odd:
(defn foo [x] {:post (%)} x)
(`[%]` works the same as (%) here -- it's just a sequence of forms to evaluate with % bound to the function result. This was a surprise to me when I tried it in the REPL!

qqq23:12:13

for implementing a forth interpreter, is vector or list better matched for storing the stack ?

noprompt23:12:30

@seancorfield yes, that was a typo.

noprompt23:12:39

thank you for catching that.

bronsa23:12:21

@qqq if you use conj/peek/pop, vectors and lists behave the same as stacks

seancorfield23:12:34

@noprompt I was genuinely surprised it "worked" -- I rarely use :pre/`:post` so I nearly always have to look up the syntax!

qqq23:12:50

@bronsa: sure, but I suspect they have different performance characteristics

bronsa23:12:52

I tend to use vectors as stacks just because pop-n and peek-n are easier to implement efficiently on vectors

noprompt23:12:01

@qqq +1 for vectors. and enjoy the concatenative journey. 🙂

bronsa23:12:07

@qqq no, all those operations are constant time on both vectors and lists

qqq23:12:31

@bronsa: I think vector is log_32 #-elemes, which is <= 5 in most caeds, but technically log n

seancorfield23:12:01

Hey @qqq If you want Clojure + Forth, take a look at https://gershwin.github.io/ 🙂

seancorfield23:12:51

I was actually sufficiently awed by Gershwin at one point that I tried it out at work and wrote a few functions in the "Forth" style of Clojure! 🙂

bronsa23:12:12

@qqq no that's not correct, conj/pop/peek on vectors are amortized constant time, not log_32, nth/assoc are log_32 on vectors

qqq23:12:16

@seancorfield: I've lost track the # of afternoons I've lost due to you providing fasicnating links.

seancorfield23:12:31

(but since it relies on a fork of Clojure and wasn't being updated, I quickly went back to pure Clojure)

qqq23:12:15

@bronsa: conj has to be log_32 time as it has to create a new node for every level of the b-tree

bronsa23:12:36

clojure's persistent vectors are not b-trees

qqq23:12:58

I thought they were btrees with 32 fanout -- if not btrees, what are they?

noprompt23:12:50

@qqq if you’re interested have a look at factor, joy, cat, and kitten in this space as well.

noprompt23:12:13

factor is a ton of fun.

bronsa23:12:26

this is the optimization that makes conj amortized constant time

bronsa23:12:55

same for pop/peek

qqq23:12:31

1. the depth of the tree is log_32 n 2. when we do a conj, we have to create a new node at each level of the tree ^-- which is the two above statements is false ? -- because if they're both true, it's log_32 n time EVERY TIME

bronsa23:12:07

2 is not true

qqq23:12:55

Instead of keeping the rightmost leaf in the tree itself, we keep a direct reference to it in the vector header: That's the last block which has been added to the vector head since last blogpost. The reference to the rightmost leaf node is called the tail.
ah

qqq23:12:59

that's clever; bronsa++

bronsa23:12:15

well I didn't come up with any of this

qqq23:12:33

thanks for correcting me on this time, I've been thinking conj was log_32 n time

bronsa23:12:49

wrt "what are they", I've heard the clj vector impl called bitmapped vector trie, not sure if there's a better name

qqq23:12:17

@seancorfield: I feel like we can get most of forth in clojure by defining macro f->, where it behaves like -> except 1. if it sees a constant (number, kw, string), it pushes it to the stack 2. all functionsin it take stack as input and produces stack as output

noprompt23:12:08

@qqq the thing is though you’ll need to come up with a way to call out how much of the stack to consume for fn’s that have multiple arities.

noprompt23:12:32

e.g. how do you interpret ["foo" "bar" "baz" str]?

bronsa23:12:11

https://twitter.com/brandonbloom/status/528262785642545153 this is still my favourite impl of stack programming in clojure :)