Fork me on GitHub
#clojure
<
2017-03-07
>
arthur00:03:15

I'm confused on how to compose recursive and iterative selectors in spector. If I want to select the 4th item in a depth first traversal of a vector represented by nested vectors so it's not this:

(select [(walker number?) (srange 3 4)] tree)

nathanmarz00:03:54

@arthur an example of what you're trying to do would be helpful

arthur00:03:25

trying to get specter back in my head so i was working through the examples in your latest blog post

arthur00:03:52

tree is the value from that post

[1 [2 [[3]] 4] [[5] 6] [7] 8 [[9]]]

arthur00:03:54

so the correct output would be 5

arthur00:03:12

I can do it this way

(nth (select [(walker number?) ] tree) 4)

arthur00:03:24

though can't then use that in a transform

nathanmarz00:03:48

first off, I'd recommend using this instead of walker:

(def TREE-VALUES
  (recursive-path [] p
    (if-path vector?
      [ALL p]
      STAY)))

nathanmarz00:03:13

walker is very brute force and will descend into anything, like maps or lists that could be at the leaves

nathanmarz00:03:24

TREE-VALUES is more precise and much more performant

arthur00:03:31

that's what I started wtih

nathanmarz00:03:43

then this: (select-any [(subselect TREE-VALUES) (nthpath 4)] tree)

arthur00:03:45

OK, so I'm not understanding something about the way select works when given a vector of selectors and how it composes them. This is something I can work on 🙂 thanks

nathanmarz01:03:19

just think of it is navigating one step at a time

arthur01:03:42

I was trying to use select rather than subselect in the vector

nathanmarz01:03:50

first it navigates to the sequence of elements found by TREE-VALUES, then to index 4 of that sequence

nathanmarz01:03:32

subselect is definitely a more advanced navigator

arthur01:03:27

I'm pondering the difference between select and subselect now

nathanmarz01:03:54

select is an operation, subselect creates a navigator

arthur01:03:36

so every time I want to use the idea of selecting inside a call to transform or select i will use subselect ?

nathanmarz01:03:19

if it's important to navigate those elements as a single sequence, then yes

nathanmarz01:03:58

subselect is very powerful but it's not needed that often in my experience

lvh01:03:01

@nathanmarz What would a navigator that grabs values in an otherwise arbitrarily nested tree of vecs and maps look like? I originally did something with MAP-VALS but that doesn’t work as well for nested values of course 🙂

lvh01:03:53

(To contrast with walker/clojure.walk: I don’t want to modify any keys.)

lvh01:03:33

I could write it with zippers, but I’m trying to learn specter.

jr01:03:38

Is codeq a dead project?

nathanmarz01:03:33

@lvh if you just want to descend into any data structure you can use walker

nathanmarz01:03:46

that will descend to map keys as well

nathanmarz01:03:14

otherwise you can be more precise by expanding on that TREE-VALUES navigator I pasted above

nathanmarz01:03:28

(using cond-path)

lvh01:03:41

Sorry; I just realized I omitted an important bit: leaf values — but I guess the answer there is TREE-VALUES as well?

nathanmarz01:03:10

that definition of TREE-VALUES is only for a tree represented with nested vectors

nathanmarz01:03:30

(cond-path vector? [ALL p] map? [MAP-VALS p] STAY STAY) would descend into all vectors and into all map values

lvh01:03:55

@nathanmarz Thanks! Why does STAY STAY work at the end? I was expecting a predicate; maybe (constantly true).

lvh01:03:36

(it did work; but I don’t think I understand why)

nathanmarz02:03:05

@lvh conditions in specter are true if the given path selects anything

nathanmarz02:03:46

STAY is guaranteed to select something (and is also the most efficient), so it's the best choice for the equivalent of :else

lvh02:03:46

And STAY always selects. Makes sense. But (constantly true) would’ve worked fine too then I presume?

lvh02:03:14

Cool. Thanks 🙂

nathanmarz02:03:37

yea that would work too

creese06:03:17

Is it possible to change a spec for test?

qqq12:03:47

Is there a short hand for

(swap! some-atom
  #(-> %
    (update-in [ ... ])
    (update-in [ ...])))

fernandohur12:03:51

Cursors are your friend @qqq

fernandohur12:03:04

Take a look at reagent.core/cursor

qqq12:03:26

@fernandohur: what? reagent as in react layer reagent?

fernandohur12:03:40

🙈 woeps, wrong channel. I though I was in #reagent for a sec

negaduck14:03:27

I have two functions that do different things, but have similar let bindings. Like (def foo [args] (let [similar-bindings-using-args] …)) and (def bar [args] (let [similar-bindings-using-args] …)). Is there a way to share those bindings between them?

qqq15:03:48

@negaduck : based on the bindings, you can turn the "args" into a map; then in both functions, you can use {:keys [ .. ] }

jstew15:03:16

I was just going to write the same thing. Extract the common bindings into a function that returns a map.

negaduck15:03:42

@qqq, @jstew, going to try this, thanks

jstew15:03:08

@qqq I don't know of a shorthand for that, but I would like to know as well. I end up updating a map multiple times in an atom pretty often.

jstew15:03:52

sometimes if I'm updating a nested map, (merge-with merge {...} {...}) comes in handy

qqq15:03:35

@jstew: it's possible I may go datascript for htis; it's getting dangerously close to the point where I just want to do db transactions for multiple updates

pseud15:03:24

Do we have any preferences wrt cassaforte / alia ?

mpenet15:03:58

yep, alia is awesome, but I might be biased 🙂

mpenet15:03:34

more seriously from the mouth of one of cassaforte's autors, cassaforte is not really developed anymore, alia is

val_waeselynck15:03:57

@qqq shameless plug: check out the supdate library https://www.github.com/vvvvalvalval/supdate

pseud15:03:29

Seems that way, couldn't get their "Getting Started" guide to work ("no matching ctor" error) and GH, Clojars & their Guide all disagreed on the most recent version 😉

pseud15:03:09

Seems like supporting Cassandra in general is a bit of a pain. Oh well, alia it is

mpenet15:03:34

it's ok, I can't complain, I have seen way worse

rauh15:03:50

I'm a happy alia user, and just last week moved my project to spandex (ES client from mpenet). Pretty happy with it. No issues.

peeja17:03:34

Is there any way to ask for the arity of a function?

peeja17:03:22

This makes me 😞

peeja17:03:40

I have a function which client code passes a zero-arg function to. I'd like to extend it to give the client's function some information, if it accepts it, but not break backwards compatibility.

peeja17:03:46

Am I SOL?

tbaldridge17:03:53

you might be able to attach metadata to it, but yeah, if you need more information than 'is a ifn?" you probably want to use protocols

tbaldridge17:03:25

Protocols are often better for higher-order-programming anyways since functions become a bit less opaque about what they are accepting.

peeja17:03:33

I mean, it's too late for that now…

tolitius17:03:48

@peeja not the prettiest way, but might do what you need:

boot.user=> (defn f1 [f2]
              (try (f2 42)
                (catch clojure.lang.ArityException ae
                  (throw (RuntimeException. "f2 should be a function that takes X arguments" ae)))))

boot.user=> (defn f2 [n] {:answer n})
boot.user=> (defn f3 [] {:answer 42})

boot.user=> (f1 f2)
{:answer 42}

boot.user=> (f1 f3)

clojure.lang.ArityException: Wrong number of args (1) passed to: user/f3
 java.lang.RuntimeException: f2 should be a function that takes X arguments

tbaldridge17:03:13

sure...and then call that on 😉

tbaldridge17:03:32

oh nvm, I see

tbaldridge17:03:10

you're adding metadata....but wait, doesn't clojure already do this?

tbaldridge17:03:37

Yeah, Clojure already does that @tolitius

tolitius17:03:54

not sure what you mean

tbaldridge17:03:25

Well it depends what you're trying to do I guess, Clojure already gives you the name and the number of functions you passed it

tbaldridge17:03:45

I guess your code would need to be expanded to do what @peeja was asking?

tolitius17:03:10

right, but @peeja wanted to figure out the arity of a function at runtime

tolitius17:03:23

unless I misunderstood

tbaldridge17:03:27

right, and there's no way to do that

tbaldridge17:03:36

I'm not sure how your code helps

tolitius17:03:13

right, so in order to still accept a wrong arity function and to have a legible error sent back he can wrap the exception

tolitius17:03:41

unless he wanted to do something different

tbaldridge17:03:00

But that does require knowing ahead of time what the arity of the function was

tolitius17:03:18

wrong arity => exception

tbaldridge17:03:19

In your code you have to know what "X" is somehow

tolitius17:03:43

X is a constant, he knows what it should be

tbaldridge17:03:47

right, so this is just a different way of attacking the problem. This code doesn't tell you what the arity is, it just gives you a better message when you get it wrong, providing you know ahead of time what the arity should be.

tolitius17:03:18

right, isn't that what he needed, @peeja ?

tolitius17:03:37

> I'd like to extend it to give the client's function some information, if it accepts it, but not break backwards compatibility

peeja17:03:52

Yeah, I don't want any exceptions

tolitius17:03:14

instead of rethrowing you can do whatever you need

peeja17:03:30

Sure, I'd just rather not rely on exception handling for normal operation

tolitius17:03:44

this is not a good solution to design, but as a temp fix until you get to use protocols, it'll do

peeja17:03:21

Alright, so, full disclosure: I'm thinking through a proposal for clojure.spec 🙂

peeja17:03:43

Changing things to protocols isn't going to fly 🙂

tolitius17:03:47

@peeja yea, definitely not to control the flow, but since there is no way to figure out the arity at runtime, you should at least know when it is wrong

xiongtx19:03:17

Does extend-protocol guarantee order when used with subtypes? E.g. suppose class Foo implements interface IFoo. Then, extending both Foo and IFoo with protocol Bar:

(extend-protocol Bar
  Foo
  (do-bar []
    "Foo-bar")

  IFoo
  (do-bar []
    "IFoo-bar"))
Is calling do-bar on an object of type Foo guaranteed to give ”Foo-bar" instead of ”IFoo-bar"?

hiredman19:03:52

protocol functions are functions, not methods

hiredman19:03:03

so you don't call do-bar

hiredman19:03:09

you call some-namespace/do-bar

xiongtx19:03:31

Sure; not really relevant though

hiredman19:03:59

I was reading extend-type

hiredman19:03:37

I don't think there is an guarantee

hiredman19:03:07

some people have asked for a prefer-method kind of thing, but rich has pushed back (I forget the reasoning)

bfabry19:03:37

if it's not written down I'd say it's an implementation detail. probably work for a specific version of clojure but might change between versions

xiongtx19:03:21

That’s interesting, b/c extending java.lang.Object gives a default. From http://www.braveclojure.com/multimethods-records-protocols/:

(extend-protocol Psychodynamics
  java.lang.String
  (thoughts [x] "Truly, the character defines the data type")
  (feelings-about
    ([x] "longing for a simpler way of life")
    ([x y] (str "envious of " y "'s simpler way of life")))
  
  java.lang.Object
  (thoughts [x] "Maybe the Internet is just a vector for toxoplasmosis")
  (feelings-about
    ([x] "meh")
    ([x y] (str "meh about " y))))

hiredman19:03:50

Ah, yes, it does walk up the tree, where you would need prefer-method is things like interfaces

timrichardt21:03:24

@hiredman, could you point to me the part of the code of clojurebot, that regulates the allowed fuctions for input?

hiredman21:03:15

https://github.com/hiredman/clojurebot/blob/master/clojurebot-eval/src/clojurebot/sandbox.clj it is an ancient part of clojurebot, lots of legacy bits, most of it doesn't apply anymore since forms are not evaluated in the same clojure runtime as clojurebot

hiredman21:03:37

it doesn't actually do much either

grav21:03:38

Promesa is awesome! I have some shared node/jvm code, and had to wrap everything in promises to get the same interface. Promesa solved that, and it also allowed me to do synchronous unit-testing because I can deref the promises on the jvm. So cool!

timrichardt21:03:19

what is the other runtime?

hiredman21:03:45

clojurebot creates an isolated(ish) classloader and loads another clojure runtime in it and runs the code there

timrichardt21:03:23

ah this security-manager thing 🙂 i have to read some manpages to understand that code. but thanks! 🙂

hiredman21:03:45

the security manager thing is also a thing, but it is another thing

hiredman21:03:30

classloader isolation allows clojurebot to load another version of clojure and evaluate code in it, and keeps the user code evaluation environment distinct from clojurebot's

hiredman21:03:51

the securitymanger is wrapped around that to limit what code evaluated in that isolated classloader can do

kwladyka21:03:52

How do you deal with java.lang.NullPointerException bugs? It says nothing.... no file, no line, no reason.

hiredman21:03:55

where do you see the npe message?

kwladyka21:03:10

Where? In the REPL 🙂

hiredman21:03:41

depend on the version of clojure you are on *e will give you a data representation of the stacktrace

hiredman21:03:30

exceptions (with the exception of when the jvm optimizes them away, and there is a flag to turn that off) all have stacktrace information on them

hiredman21:03:37

some tools just don't show it to you

kwladyka21:03:47

helps a little but still it is hard to guess

hiredman21:03:51

the last exception thrown is bound to *e in the repl

hiredman21:03:13

(.printStacktrace *e) will print a more traditional java style stacktrace

hiredman21:03:24

clojure.stacktrace has some tools

creese21:03:40

In code, I have spec called ::credit-account defined as (set (keys (config/chart-of-accounts))). The integration test uses a different chart of accounts to test. What is the best way to redefine the spec so the test can pass?

kwladyka21:03:05

but ok, *e help a lot acutally

hiredman21:03:13

people have complained a lot about clojure stacktraces in the past, and unfortunately this has caused a lot of tooling to decide to hide or not display them by default, which is terrible when you know what you are doing and can read stacktraces

kwladyka22:03:10

heh showing java.lang.NullPointerException only is not helpful in any way 🙂

kwladyka22:03:01

so simple thing and i miss that

tbaldridge22:03:40

Traditionally adding more information to error also has the nasty habit of making some code slower (due to inlining limits and the like).

tbaldridge22:03:33

Esp, with NPEs where they often crop up when you call a function and the local that function is in is nil. Stuff like that is really hard to provide more information about as it affects every function call site.

tbaldridge22:03:44

But in that case a line number is often fairly helpful.

seancorfield22:03:58

Another fun thing to watch out for is that the JVM will optimize away the stacktrace from a commonly occurring exception — so if the same code throws NPE repeatedly, subequent throws will “mysteriously” have no stacktrace even with *e!

seancorfield22:03:40

-XX:-OmitStackTraceInFastThrow will be your friend (as a JVM option)

qqq23:03:12

:let / :when works inside for ; are they also suppsoed to work under do-seq ? (I exepct them to, but I can't find it in clojure docs)

jr23:03:01

looks like doseq supports the same bindings

hiredman23:03:45

user=> (doc doseq)
-------------------------
clojure.core/doseq
([seq-exprs & body])
Macro
  Repeatedly executes body (presumably for side-effects) with
  bindings and filtering as provided by "for".  Does not retain
  the head of the sequence. Returns nil.
nil
user=> 

zerocooljs23:03:31

Hi all quick question how I can create a spec that allow nil or string values and for both return true

zerocooljs23:03:01

Right now I have this but not work:

(def non-empty-string? (s/and string? (complement empty?)))
(s/def :name (or nil? non-empty-string?))

qqq23:03:53

https://github.com/jonase/kibit <-- where in this code does it parse the clj fiels?

qqq23:03:11

I see wehre it defines the rules, where it runs unifirication

qqq23:03:31

but what I don't see is: where are the clj files parsed

zerocooljs23:03:37

@tanzoniteblack that works thank you!

qqq23:03:49

@tanzoniteblack : I see it, thanks!