speculative

https://github.com/borkdude/speculative
borkdude 2018-10-22T07:31:06.000100Z

so direct linking does take care of fdefs not being checked when clojure calls core internally

borkdude 2018-10-22T07:31:37.000100Z

I wonder if there’s a way to have fdefs, but having them faster than right now, or only print a warning instead of raising an exception

borkdude 2018-10-22T08:17:46.000100Z

hmm, (=) seems to work in cljs, it returns true, but you’ll also get a warning

borkdude 2018-10-22T08:19:15.000100Z

maybe we could leave out/disable specs that only check for any?, (s/* any?) or (s/+ any?) for performance sake. they check nothing, but they make things slower. e.g. = is important to be fast I think?

borkdude 2018-10-22T08:42:04.000100Z

WIP for apply: https://github.com/borkdude/speculative/commit/23c85e675127e3793f620315682543e8138929d8 Tests pass on clj but not in cljs/planck.

borkdude 2018-10-22T08:43:23.000100Z

plk:

ERROR in (apply-test) (d@file:23:179)
Uncaught exception, not in assertion.
expected: nil
  actual: #object[RangeError RangeError: Maximum call stack size exceeded.]
However, when I paste the expression in the REPL, I get a different error

borkdude 2018-10-22T08:45:14.000100Z

oh wow…

$ plk -A:test
ClojureScript 1.10.339
cljs.user=> (require '[speculative.core])
nil
cljs.user=> (require '[clojure.spec.test.alpha :as stest])
nil
cljs.user=> (stest/instrument `apply)
Maximum call stack size exceeded.
Maximum call stack size exceeded.

borkdude 2018-10-22T08:48:48.000100Z

Same error as here: https://clojurians.slack.com/archives/CDJGJ3QVA/p1540142591000100

slipset 2018-10-22T09:15:27.000100Z

Well, at least speculative is proving some worth 🙂

mfikes 2018-10-22T12:01:29.000100Z

Nice :)

borkdude 2018-10-22T12:21:43.000100Z

bumped into this one on my work project: https://github.com/slipset/speculative/pull/31

borkdude 2018-10-22T12:26:01.000100Z

rule of thumb: accept ifn?, return fn? ?

borkdude 2018-10-22T12:28:38.000100Z

Not sure what’s up with github today. I get 404s on objects I created just a minute ago.

slipset 2018-10-22T12:43:01.000100Z

Github is in trouble today

slipset 2018-10-22T12:43:24.000100Z

I commented on your pr. An example where ifn? is valid for reduce would be appreciated 🙂

slipset 2018-10-22T12:43:37.000100Z

Also for my understanding.

mfikes 2018-10-22T12:47:49.000100Z

WRT GitHub, I pushed a revision to Planck last night and it wouldn’t show up in the GitHub UI. It finally resolved itself this morning.

borkdude 2018-10-22T12:49:53.000100Z

We have this:

(deftype SqlReduce [conn query params query-timeout fetch-size ptf]
  clojure.lang.IReduce
  (reduce [this f]
    (.reduce ^clojure.lang.IReduceInit this f (f)))
  clojure.lang.IReduceInit
  (reduce [this f init]
    (trace "DB: reducing sql query" query params)
    (jdbc/with-db-transaction [tx conn]
      (let [rfn (^:once fn* [coll]
                 (let [coll (if ptf (pmap ptf coll) coll)]
                   (reduce f init coll)))
            opts {:fetch-size fetch-size
                  :timeout query-timeout
                  :result-type :forward-only
                  :concurrency :read-only}
            prepared (jdbc/prepare-statement
                      (:connection tx) query opts)
            sql-params (cons prepared params)]
        (jdbc/query tx sql-params {:result-set-fn rfn})))))
when I did some REPL inspection, f was some object that implemented IFn, I’m not sure about the implementation, but we’ve been using this for years now

borkdude 2018-10-22T12:50:35.000100Z

I can look into more details

borkdude 2018-10-22T12:50:48.000100Z

maybe it’s something transducer specific

slipset 2018-10-22T12:52:57.000100Z

merged

mfikes 2018-10-22T12:53:38.000100Z

Whoah, I didn’t realize that this doesn’t work in Clojure: ([1] 7 :nf)

mfikes 2018-10-22T12:53:58.000200Z

(Related to the ifn? / reduce question)

mfikes 2018-10-22T12:54:21.000100Z

cljs.user=> (reduce [7] 0 [11])
7
cljs.user=> (reduce [7] 1 [11])
11

mfikes 2018-10-22T12:55:20.000100Z

I think I’ve always assumed that things like (#{2} 1 7) work. 😞

borkdude 2018-10-22T12:58:26.000100Z

thanks for the example, I’ll stop digging 😉

mfikes 2018-10-22T12:59:08.000100Z

But, interestingly, neither of those work in Clojure. Perhaps there is a JIRA in the Clojure stack somewhere for that.

borkdude 2018-10-22T13:11:03.000100Z

I think I found it, when you call vec on an eduction, the reducing function will be some kind of object

borkdude 2018-10-22T13:13:50.000100Z

so deftype methods are also checked by the s/fdef. nice. I guess…

borkdude 2018-10-22T13:15:00.000100Z

so example: (vec (eduction (map inc) [1 2 3]))

borkdude 2018-10-22T13:15:40.000100Z

but this won’t be caught by our spec because of direct linking

borkdude 2018-10-22T13:16:50.000100Z

> borkdude [9:34 AM] > Is it possible to have clojure.spec not throw exceptions on fdef violations, but only print “fdef error, line so and so” and then continue as always? > alexmiller [3:08 PM] > no

mfikes 2018-10-22T13:32:27.000100Z

Hmm. That could be a nice feature. That’s what we ended up doing with :checked-arrays with :warn vs. :error

mfikes 2018-10-22T13:35:08.000100Z

FWIW, the “not-found” arity for map lookup works in Clojure: ({0 2} 1 :nf)

borkdude 2018-10-22T15:47:17.000100Z

yeah, we could run a fork of spec in the tests if that detects failures faster on the coalmine thing

borkdude 2018-10-22T15:47:55.000100Z

do we stall work on JIRA patches for clojurescript, or maybe wrap in a #?(:clj ...) for now?

borkdude 2018-10-22T16:10:09.000100Z

I made a PR with a conditional for the tests, until the JIRA ticket is fixed

slipset 2018-10-22T16:15:49.000100Z

Merged. I’ll do the same with my thing for merge/merge-with

borkdude 2018-10-22T16:29:05.000100Z

detected a violation of the merge-with spec in secretary (client side routing):

(route-matches [_ route]
       (when-let [[_ & ms] (re-matches* re route)]
         (->> (interleave params (map decode ms))
              (partition 2)
              (merge-with vector {})))))

borkdude 2018-10-22T16:30:31.000100Z

Expound output:

Function arguments↵
↵
  (... ... ())↵
           ^^↵
↵
should satisfy↵
↵
  map?↵
↵
or↵
↵
  nil?↵
↵

borkdude 2018-10-22T16:33:18.000100Z

I’m also getting an error on merge:

core.cljs:2045 Uncaught Error: Invalid arity: 24
    at Function.G__10206 [as call] (core.cljs:2045)
    at Function.re_com.box.box_base.cljs$core$IFn$_invoke$arity$variadic (box.cljs:119)

borkdude 2018-10-22T16:34:00.000100Z

core.cljs:2045. I’m not sure what this has to do with spec

borkdude 2018-10-22T16:35:23.000100Z

maybe it’s the call to apply

borkdude 2018-10-22T16:36:38.000100Z

ok, when I (stest/unstrument [merge-with merge]) my client side app also works

borkdude 2018-10-22T16:37:49.000100Z

maybe we should also put conditionals around those specs proper, until they work properly in cljs…

borkdude 2018-10-22T16:39:30.000200Z

hmm, client side gets reeeeallly slow.

mfikes 2018-10-22T16:45:22.000100Z

It seems like using associative? is a reasonable relaxation

mfikes 2018-10-22T17:05:16.000100Z

But does this case involve an empty list?

borkdude 2018-10-22T17:14:37.000100Z

hmm, I was wrong. vector is the function here.

borkdude 2018-10-22T17:14:45.000100Z

I take back what I said 😉

borkdude 2018-10-22T17:18:44.000100Z

what secretary is doing is probably something like (merge-with vector {} {:a 1} {:a 2})

borkdude 2018-10-22T17:21:54.000100Z

I got an error on merge-with, but now I’m not sure what triggered it. sorry for the noise

borkdude 2018-10-22T17:23:37.000100Z

I think it works like this: (merge-with vector {} '([:a 1] [:b 2])). works in cljs, but not in clj.

borkdude 2018-10-22T17:24:26.000100Z

so it’s not even an associative? but it works 😉

borkdude 2018-10-22T17:25:13.000100Z

(merge-with vector {} '([:a 1] [:b 2] [:a 2])) ;;=> {:a [1 2], :b 2}

mfikes 2018-10-22T17:29:03.000100Z

Right, @borkdude this might help understand:

(merge {} (seq {:a 1, :b 2}))

mfikes 2018-10-22T17:31:08.000100Z

Actually, this is closer to the facet that is important:

(conj {} (seq {:a 1, :b 2}))
Digging up why that is supposed to work…

borkdude 2018-10-22T17:36:17.000100Z

yeah get it. probably we should allow a seq of MapEntry-s and on cljs a seq of 2-vectors

mfikes 2018-10-22T17:38:01.000100Z

Even ClojureScript only allows non-`MapEntry`’s in order to avoid breaking code that got used to MapEntry being represened as 2-vectors

mfikes 2018-10-22T17:39:16.000100Z

I vaguely recall that (conj {} (seq {:a 1, :b 2})) only works due to an internal implementation detail (for some other internal use case). I don’t think it is really meant to be used by application code.

mfikes 2018-10-22T17:39:55.000100Z

At the end of the day, though, it gets hard to spec code that accidentally relies on implementation details. For example (+ :what-the-fuck)

borkdude 2018-10-22T17:47:44.000100Z

MapEntry was only introduced later in ClojureScript I believe?

borkdude 2018-10-22T17:48:28.000100Z

https://dev.clojure.org/jira/browse/CLJS-2013

mfikes 2018-10-22T17:49:23.000100Z

Right, so for a long time, 2-vectors were used as a substitute. But, that didn’t stop code from then using them as vectors.

mfikes 2018-10-22T17:50:04.000100Z

And, there was not much that could be done about the consequence that (empty (first {:a 1})) used to return an empty vector

borkdude 2018-10-22T17:52:21.000100Z

so now users of our lib can’t use secretary. what to do? change the spec or PR secretary? something’s gotta give

borkdude 2018-10-22T17:53:07.000100Z

I guess partition 2 could have been written like (apply hash-map) here: https://github.com/gf3/secretary/blob/1f2036a694e49f58a97c9401878602148f9d6310/src/secretary/core.cljs#L251

borkdude 2018-10-22T17:54:19.000100Z

and how do you explain such a PR? eh yeah, we wrote a spec your code is offending. this seems the other way around 😉

mfikes 2018-10-22T17:55:06.000100Z

Right, IMHO, this will have to almost be a philosophical stance that would need to be taken by the library. Should it spec the intent, or should it bend over backwards to characterize the current implementation, or is there some reasonable balance.

mfikes 2018-10-22T17:56:21.000100Z

For example, my opinion is that using associative? instead of map? in the merge spec might be viewed as a reasonable relaxation.

slipset 2018-10-22T17:56:32.000100Z

First thought is that we should spec the intent.

slipset 2018-10-22T17:57:21.000100Z

It’s a bit like with Alex debacle when spec’ing require.

mfikes 2018-10-22T17:57:28.000100Z

Exactly

mfikes 2018-10-22T17:58:12.000100Z

But, I also hold the view that if you don’t try to spec the intent, then you are really saying that the spec is the entire transitive closure of the implementation of any given core fn. 😞

slipset 2018-10-22T17:58:52.000100Z

I also think a pr against secretary would in place. Especially it their use is somewhat strange.

mfikes 2018-10-22T17:59:37.000100Z

Maybe speculative can afford to spec the intent, while core can’t. But that still leaves the problem that certain users would be unable to use speculative because of any one offending function anywhere in their code.

slipset 2018-10-22T17:59:57.000100Z

And there is this thing called Hyrums law, which I’m struggling a bit with ATM with Eastwood.

borkdude 2018-10-22T18:00:27.000100Z

Right. At the same time users may be convinced that their idioms should follow the intent of the functions.

mfikes 2018-10-22T18:00:31.000200Z

Oh, cool, I’m glad there is a name for the concept 🙂

slipset 2018-10-22T18:01:26.000100Z

Learnt it from @andy.fingerhut :)

borkdude 2018-10-22T18:01:27.000100Z

luckily users can always unstrument functions, but it would be even nicer if you could say: I only want to see errors triggered by me, not by libraries

mfikes 2018-10-22T18:01:44.000100Z

An interesting concept is on the flip side: If you have clients exhibiting Hyrum’s law, then it makes it really difficult to revise the implementations of core functions. ClojureScript’s aget is probably the most famous example.

slipset 2018-10-22T18:01:56.000100Z

@borkdude that would be nice (if possible)

borkdude 2018-10-22T18:02:25.000100Z

@slipset that would also solve the performance problem I’m now seeing on client and server

slipset 2018-10-22T18:02:48.000100Z

@mfikes especially if you value backwards compatibility.

mfikes 2018-10-22T18:02:52.000100Z

You certainly can’t have it half way in ClojureScript: Instumentation is implemented by replacing the function being instrumented with a new version.

borkdude 2018-10-22T18:03:31.000100Z

that’s why I opted for a warning instead of a “stop the system” error

borkdude 2018-10-22T18:03:57.000100Z

“yeah, I know secretary has this weird thing, let’s move on”

mfikes 2018-10-22T18:03:58.000100Z

I bet core will end up in a position that it is impossible to spec core because of Hyrum’s law.

slipset 2018-10-22T18:04:28.000100Z

So we’re saving them tons of work by figuring that out for them :)

borkdude 2018-10-22T18:04:51.000100Z

I guess you could say in the exception: on which ns/file was this triggered, if this in a whitelist, it’s a real exception, else a warning

borkdude 2018-10-22T18:04:57.000200Z

that would be awesome for speculative probably

borkdude 2018-10-22T18:06:12.000100Z

and that’s something that would be possible in cljs I think

mfikes 2018-10-22T18:08:15.000100Z

Perhaps there is a chance that, if speculative (or something like it) became viewed as useful, then library owners would feel compelled to fix their code.

mfikes 2018-10-22T18:08:32.000100Z

It seems to involve a Catch 22 though 🙂

borkdude 2018-10-22T18:08:58.000100Z

why isn’t spec a library in cljs?

mfikes 2018-10-22T18:09:22.000100Z

Because when spec was ported to ClojureScript, it was not yet a library.

mfikes 2018-10-22T18:09:33.000100Z

Perhaps I have that wrong…

borkdude 2018-10-22T18:09:41.000100Z

ok… if it was a library it would be easier to patch 🙂

slipset 2018-10-22T18:10:35.000100Z

I would still argue that we’d be doing both core and the community a favor by opening prs against projects which kind’a violates the intents of the functions.

slipset 2018-10-22T18:11:13.000100Z

Although by that, not saying that having specs warn instead of error is a bad idea.

mfikes 2018-10-22T18:11:14.000100Z

Well, yeah, even from the human standpoint of trying to understand code, you pretty much expect merge to return a map

mfikes 2018-10-22T18:11:39.000100Z

(merge ()) works, but, WTF.

borkdude 2018-10-22T18:11:53.000100Z

because it’s an empty seq of map-entries/2-tuples 😉

mfikes 2018-10-22T18:12:13.000200Z

(merge 1) also works

borkdude 2018-10-22T18:12:22.000200Z

lol

borkdude 2018-10-22T18:12:31.000100Z

ok, let’s do the PR route then

mfikes 2018-10-22T18:12:41.000200Z

You can merge anything if it is a single argument 🙂

borkdude 2018-10-22T18:12:53.000100Z

we’re not going to relax specs unless there is a compelling reason, not to support edge cases in the wild

borkdude 2018-10-22T18:13:14.000100Z

seems like a plan?

borkdude 2018-10-22T18:13:32.000100Z

because we’re hoping people will make PRs to make the code more readable

mfikes 2018-10-22T18:13:42.000200Z

I do feel a bit odd asking a library to change working code. Perhaps I can get over that feeling 🙂

borkdude 2018-10-22T18:13:55.000100Z

we can try 😉

borkdude 2018-10-22T18:14:04.000100Z

hi, speculative police here.

mfikes 2018-10-22T18:14:26.000100Z

But, certainly, speculative can easily take the approach of leaning towards specing intent

mfikes 2018-10-22T18:15:14.000100Z

I was thinking that, in Replete (which I view as a learning tool, really), it would be interesting for it to ship with speculative, perhaps even enabled, so that newbies get better feedback when they call (merge 1)

borkdude 2018-10-22T18:15:38.000100Z

yeah, makes sense

mfikes 2018-10-22T18:16:33.000100Z

Ahh here is the real problem. If speculative catches an error somewhere in core itself, or in core.async, I bet there is no chance of getting that code revised

mfikes 2018-10-22T18:20:20.000100Z

Having said that, this patch was accepted https://github.com/clojure/core.async/commit/cbf8778bd7b24a3102056d21eab1f1cc2860dd95

mfikes 2018-10-22T18:21:28.000100Z

But, arguably, that was in support of the :checked-arrays feature actually in ClojureScript proper

slipset 2018-10-22T18:22:36.000100Z

It would be somewhat tongue in cheek to have certain specs relaxed in speculative b/c quirks in core with comments explaining exactly why the relaxation was in place.

borkdude 2018-10-22T18:23:03.000100Z

I’m starting to see why secretary did this.

borkdude 2018-10-22T18:23:25.000100Z

(->> '(:* cat :* bat) (apply hash-map)) ;;=> {:* bat}

mfikes 2018-10-22T18:24:08.000100Z

That code seems fine

borkdude 2018-10-22T18:24:14.000100Z

(->> '(:* cat :* bat)  (partition 2)) ;;=> ((:* cat) (:* bat))

borkdude 2018-10-22T18:24:23.000100Z

no, it loses the cat value

borkdude 2018-10-22T18:25:07.000100Z

so I think this usage is actually legit

mfikes 2018-10-22T18:25:29.000100Z

Losing the cat value is what is supposed to happen, though

mfikes 2018-10-22T18:25:43.000200Z

(Per the docstring for hash-map)

borkdude 2018-10-22T18:26:08.000100Z

no it’s not (in secretary):

FAIL in (route-matches-test) (:)
splats
expected: (= (s/route-matches "*.*" "cat.bat") {:* ["cat" "bat"]})
  actual: (not (= {:* "bat"} {:* ["cat" "bat"]}))
That’s why he isn’t using hash-map

borkdude 2018-10-22T18:27:15.000100Z

there may be another way, but I see how he ended up here

mfikes 2018-10-22T18:27:44.000100Z

Yeah, perhaps in seeking a solution, he discovered something that works (accidentally), and went with it

slipset 2018-10-22T18:28:36.000100Z

Maybe the lib should’a been called well-actually?

mfikes 2018-10-22T18:30:49.000100Z

That Hyrum’s Law explanation is pretty good. One gem is “the interface has evaporated: the implementation has become the interface”

mfikes 2018-10-22T18:32:30.000100Z

Given that, yeah, using aget to access JavaScript object properties makes complete sense

mfikes 2018-10-22T18:33:39.000100Z

That clarifies my thinking: speculative really has to document some level of intent, otherwise speculative ends up being core itself

mfikes 2018-10-22T18:34:46.000100Z

(Meaning speculative would end up specifying the core implementation, and thus might as well be core—it’s the problem where, if you want to know if a function is being called correctly, one way to find out is to call it and see how it behaves for your inputs)

borkdude 2018-10-22T18:35:10.000100Z

to make it fit our spec, secretary could do:

(route-matches [_ route]
        (when-let [[_ & ms] (re-matches* re route)]
          (->> (interleave params (map decode ms))
               (partition 2)
               (reduce
                (fn [acc [k v]]
                  (update acc k (fn [o]
                                  (cond (not o) v
                                        (not (vector? o)) [o v]
                                        :else (conj o v)))))
                {}))))
The merge-with ain’t looking so bad 😉

borkdude 2018-10-22T18:35:40.000100Z

(and to make the tests of secretary pass)

mfikes 2018-10-22T18:37:14.000100Z

If you take the stance that speculative specs the intent, Hyrum’s Law implies it can never really be used broadly. Unless spec had some way of letting you only apply it to the code you are writing in your project.

mfikes 2018-10-22T18:37:40.000100Z

Sorry, I’m just coming around to the point that Michiel made

borkdude 2018-10-22T18:37:56.000100Z

the clojurescript merge-with doesn’t speak about map-entries at all. it just takes first and second from a seq

borkdude 2018-10-22T18:38:39.000200Z

I think speculative should have a clear rationale

borkdude 2018-10-22T18:38:54.000100Z

first I thought the rationale was: catching errors at dev time

borkdude 2018-10-22T18:39:03.000200Z

later: we could use this to test core

borkdude 2018-10-22T18:39:18.000100Z

now: creating PRs for libraries to make them more compliant

borkdude 2018-10-22T18:39:51.000100Z

so yeah, let’s settle on something so people know why they might want to use this

mfikes 2018-10-22T18:39:56.000100Z

Ahh, that’s a great example of how it make it difficult to optimize ClojureScript. In merge-with, that should actually be key and val not, first and second

slipset 2018-10-22T18:40:14.000100Z

My first perspective was better error-messages at dev-time.

slipset 2018-10-22T18:40:41.000100Z

Secondary, it turns out to be some sort of lint tool for libs.

mfikes 2018-10-22T18:41:01.000100Z

Yeah, that’s what I really want. I want it to catch silly arguments I might happen to be passing. Not because I don’t understand core, but because I simply made a mistake.

slipset 2018-10-22T18:41:03.000100Z

It says, Hey, don’t do that.

borkdude 2018-10-22T18:42:14.000100Z

right. so if we settle on that rationale, we should only or primarily be focussing on the :args part of the specs, that’s something we could agree on

slipset 2018-10-22T18:42:20.000100Z

And if the perspective is better error-msgs, then it would be mostly aimed at beginners trying out Clojure, so the compatibility with every lib out there is not such a big point?

borkdude 2018-10-22T18:42:42.000100Z

also we could agree that using any? only specs are not a part of speculative, as they only slow things down and catch nothing

slipset 2018-10-22T18:42:57.000100Z

Agreed.

borkdude 2018-10-22T18:43:03.000100Z

@slipset don’t agree there. I want to use it myself during dev.

slipset 2018-10-22T18:43:21.000100Z

Disagreement is fine.

slipset 2018-10-22T18:43:34.000100Z

It’s a vehicle for a better solution.

borkdude 2018-10-22T18:44:30.000100Z

you may convince me not to use it of course 😉

slipset 2018-10-22T18:44:31.000100Z

I would suggest though, that a :ret is a nice thing if it doesn’t cause problems.

borkdude 2018-10-22T18:44:47.000100Z

:ret only works if you’re generatively testing

slipset 2018-10-22T18:45:00.000100Z

Since it could be used for testing core.

slipset 2018-10-22T18:45:35.000100Z

And there is a gist out there somewhere which turns on :ret checking outside of gem-test.

borkdude 2018-10-22T18:45:43.000200Z

yes, some libs do it

borkdude 2018-10-22T18:45:58.000200Z

but in normal spec usage, it doesn’t do anything

mfikes 2018-10-22T18:46:14.000100Z

:ret does at least show up in doc output

slipset 2018-10-22T18:46:19.000100Z

Anyways I need to get of the phone and work out a bit.

borkdude 2018-10-22T18:47:11.000100Z

have a nice workout

mfikes 2018-10-22T18:47:27.000100Z

The main unsolved problem I see is that, if I agree with the speculative rationale, and I want to use it, but too many of my favorite libs trigger errors, preventing me from using it. Perhaps in that case, I as a user of speculative would be compelled to work with my favorite libs to get them fixed.

borkdude 2018-10-22T18:48:54.000100Z

perhaps, but the initial experience is important :thinking_face:

borkdude 2018-10-22T18:49:49.000100Z

for a closed environment like mariacloud or replete it’s cool

mfikes 2018-10-22T18:50:35.000100Z

Yes. Especially if speculative takes on a didactic flavor, it should definitely end up in Replete.

mfikes 2018-10-22T18:50:49.000100Z

(And enabled by default.)

mfikes 2018-10-22T18:51:42.000100Z

Cool. I’m now satisfied that we can do something that core really can’t do, but still benefit the community.

borkdude 2018-10-22T18:52:02.000200Z

ok, that could be a rationale… focusing on intent and learning of core spec functions?

mfikes 2018-10-22T18:52:42.000100Z

Yeah, probably not a great analogy but like training wheels Or perhaps a better analogy, a safety net

borkdude 2018-10-22T18:53:01.000100Z

I was hoping to use this as a more general dev tool though

mfikes 2018-10-22T18:53:08.000100Z

Me too

mfikes 2018-10-22T18:53:28.000100Z

I would have it enabled in my day-to-day work.

borkdude 2018-10-22T18:53:39.000100Z

Performance isn’t a problem in learning tools. So we can adapt the rationale to not deal with those problems 😜

mfikes 2018-10-22T18:54:27.000100Z

Another problem unsolved as of yet: If core itself triggers an error. Hopefully it wont.

borkdude 2018-10-22T18:54:47.000100Z

it doesn’t because of direct linking. I’m not sure about cljs

borkdude 2018-10-22T18:55:16.000100Z

does cljs support direct linking?

borkdude 2018-10-22T18:55:54.000100Z

oh the function replacement right?

mfikes 2018-10-22T18:55:56.000100Z

No… in the sense that, when you call a function you are always looking it up via something like cljs.core.inc

mfikes 2018-10-22T18:56:59.000100Z

I guess :advanced could get wonky… I haven’t thought about that very much

mfikes 2018-10-22T19:44:29.000100Z

It will be interesting if the patch in https://dev.clojure.org/jira/browse/CLJS-2943 can break any code out there that is passing vectors in.

borkdude 2018-10-22T19:52:32.000100Z

you can test today by trying the secretary example

borkdude 2018-10-22T19:53:43.000100Z

it could also be part of the rationale of speculative to bring clojure and clojurescript more together… and to bring code written in libraries more to the level of multi-platform

borkdude 2018-10-22T19:54:39.000100Z

so our specs only support code that runs in both (as much as possible)

borkdude 2018-10-22T19:55:00.000100Z

(thinking out loud)

borkdude 2018-10-22T20:14:02.000100Z

@mfikes have you tested the secretary thing? if you can paste your merge-with here I can give it a try 🙂

borkdude 2018-10-22T20:14:50.000100Z

I think it will fail anyway: (key [:a 1]) ;;=> No protocol method IMapEntry.-key defined for type cljs.core/PersistentVector: [:a 1]

mfikes 2018-10-22T20:15:02.000100Z

Here

(defn merge-with
  "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(s)
  from the latter (left-to-right) will be combined with the mapping in
  the result by calling (f val-in-result val-in-latter)."
  [f & maps]
  (when (some identity maps)
    (let [merge-entry (fn [m e]
                        (let [k (key e) v (val e)]
                          (if (contains? m k)
                            (assoc m k (f (get m k) v))
                            (assoc m k v))))
          merge2 (fn [m1 m2]
                   (reduce merge-entry (or m1 {}) (seq m2)))]
      (reduce merge2 maps))))

mfikes 2018-10-22T20:15:43.000100Z

Yeah, that is what I thought might happen. Fucking Hyrum.

borkdude 2018-10-22T20:15:44.000100Z

I guess you can run this change on your coal-mine

mfikes 2018-10-22T20:16:12.000100Z

Yeah, I have that queued. Travis is backlogged right now.

mfikes 2018-10-22T20:17:29.000100Z

In cases like these, I will often submit a ticket to the affected project. It is this bit of code here, right? https://github.com/gf3/secretary/blob/1f2036a694e49f58a97c9401878602148f9d6310/src/secretary/core.cljs#L252

borkdude 2018-10-22T20:39:50.000100Z

true

borkdude 2018-10-22T20:44:53.000100Z

Maybe we should rename this project to hyrum 😅

mfikes 2018-10-22T20:46:20.000100Z

Hah!

mfikes 2018-10-22T20:47:26.000100Z

I just learned something completely new about Clojure from Alex:

(keys (seq {:a 1, :b 2}))
is intentionally supported. In other words, if you have a seq? of things that satisfy map-entry? it can be used in lieu of map?

mfikes 2018-10-22T20:47:47.000200Z

<Mind blown>

mfikes 2018-10-22T20:48:06.000100Z

Only in certain APIs, like keys, or conj ....

slipset 2018-10-22T20:48:21.000100Z

The fact that you two got involved in this project is such an unexpected, positive thing!

slipset 2018-10-22T20:49:00.000100Z

Also, the discussions, tickets and knowledge sharing that happens because of this is so cool!

borkdude 2018-10-22T20:49:10.000100Z

does that have to do with keys destructuring on seqs? (let [{:keys [:a :b]} '(:a 1 :b 2)] [a b])

mfikes 2018-10-22T20:50:17.000100Z

I don't know. I'm still recovering from brain explosion.

mfikes 2018-10-22T20:50:22.000100Z

(keys (filter (comp pos? val) {:a 0, :b 1}))

borkdude 2018-10-22T20:51:01.000100Z

@mfikes I think we may want to use a more relaxed spec for map? in functions like merge then. The only thing that secretary then has to do is to convert those 2-colls to proper map-entries.

slipset 2018-10-22T20:51:43.000100Z

There’s a discussion about this in #cljs-dev right now.

mfikes 2018-10-22T20:52:04.000100Z

Yeah, go lurk if not already in that channel 🙂

slipset 2018-10-22T20:52:48.000100Z

It’s quite interesting. I feel like a kid listening in on the grownups having serious discussions :)

mfikes 2018-10-22T20:54:38.000100Z

Me too :)

borkdude 2018-10-22T20:58:12.000100Z

heissenspec

borkdude 2018-10-22T20:58:29.000100Z

(I’m watching Breaking Bad)

borkdude 2018-10-22T20:58:50.000100Z

@slipset yeah, this project is fun.

borkdude 2018-10-22T21:02:10.000100Z

@mfikes

cljs.user=&gt; (MapEntry. 1 2)
            ^
WARNING: Wrong number of args (2) passed to MapEntry at line 1
[1 2]

borkdude 2018-10-22T21:02:25.000100Z

what’s this?

borkdude 2018-10-22T21:06:06.000100Z

(merge-with vector {} (sequence (comp (partition-all 2) (map (fn [[k v]] (MapEntry. k v)))) [:a 1 :a 2 :b 3]))
seems to work

borkdude 2018-10-22T21:08:18.000100Z

just shoving (map (fn [[k v]] (MapEntry. k v))) into the -&gt;&gt; also works of course

mfikes 2018-10-22T21:11:39.000100Z

@borkdude MapEntry has a hash value as well

mfikes 2018-10-22T21:12:00.000100Z

You can unit it with nil

borkdude 2018-10-22T21:12:45.000100Z

why’s the contructor different from Clojure?

mfikes 2018-10-22T21:17:54.000100Z

Does Clojure define it or Java (I’m afk and can’t recall). In ClojureScript it is a deftype

borkdude 2018-10-22T21:18:17.000200Z

Java

mfikes 2018-10-22T21:18:54.000100Z

ClojureScript has no choice, given it is a deftype

borkdude 2018-10-22T21:20:02.000100Z

it does seem a little bit inconsistent and inconvenient that Clojure allows you to use 2-vecs instead of MapEntry in some places and in others it doesn’t. Also given that MapEntries are probably not intended to be created by end users

mfikes 2018-10-22T21:21:07.000100Z

Yeah. ClojureScript has just been trying to follow Clojure

borkdude 2018-10-22T21:23:43.000100Z

makes sense

slipset 2018-10-22T21:24:12.000100Z

Just tried to write a spec for assoc 😕

slipset 2018-10-22T21:24:23.000100Z

Function signature:

slipset 2018-10-22T21:24:25.000100Z

(assoc map key val)(assoc map key val &amp; kvs)

slipset 2018-10-22T21:24:39.000100Z

From the docstring

slipset 2018-10-22T21:24:42.000100Z

When applied to a vector, returns a new vector that
  contains val at index. Note - index must be &lt;= (count vector).

borkdude 2018-10-22T21:24:46.000100Z

map = associative?

slipset 2018-10-22T21:26:09.000100Z

And there we have a diff between clojurescript and clojure

slipset 2018-10-22T21:26:24.000100Z

23:24 $ clj
Clojure 1.10.0-RC1
user=&gt; (assoc nil 'lol)
Evaluation error (ArityException) at clojure.lang.AFn.throwArity (AFn.java:429).
Wrong number of args (2) passed to: clojure.core/assoc--5316
user=&gt; (assoc nil 'lol 'lol)
{lol lol}
user=&gt; (assoc nil 'lol 'lol 'lol)
Evaluation error (IllegalArgumentException) at clojure.core/assoc--5316 (core.clj:195).
assoc expects even number of arguments after map/vector, found odd number
user=&gt;

slipset 2018-10-22T21:27:06.000100Z

whereas Planck says

slipset 2018-10-22T21:27:11.000100Z

23:25 $ plk
ClojureScript 1.10.339
cljs.user=&gt; (assoc nil 'lol)
            ^
WARNING: Wrong number of args (2) passed to cljs.core/assoc at line 1
{lol nil, nil nil}
cljs.user=&gt; (assoc nil 'lol 'lol)
{lol lol}
cljs.user=&gt; (assoc nil 'lol 'lol 'lol)
{lol nil}
cljs.user=&gt;

slipset 2018-10-22T21:27:39.000100Z

And with that, I’ll hit the sack!

borkdude 2018-10-22T21:28:05.000100Z

I would say this is a bug in cljs: (assoc nil 'lol 'lol 'lol)

borkdude 2018-10-22T21:28:21.000200Z

goodnight @slipset

mfikes 2018-10-22T22:07:22.000100Z

FWIW, here is the actual genesis of the intent vs. implementation and specs thing: https://youtu.be/6ftW8UwwP_4?t=2359

mfikes 2018-10-22T22:07:31.000100Z

(At least recently. :)