Fork me on GitHub
#clojure
<
2018-09-19
>
idiomancy02:09:13

is there anything like delay but that doesn't cache?

enforser02:09:07

maybe future is what you're looking for?

idiomancy02:09:48

pretty sure that caches too

enforser02:09:55

what are you trying to do?

idiomancy02:09:19

ahh, I mean in this context I really could do this with just making it a function call

idiomancy02:09:54

I don't really know why I'm attached to the de-refing syntax. I guess it's just consistent with the other re-frame subscription stuff

seancorfield03:09:44

@idiomancy You could always reify IDerefable (or whatever the equivalent is in cljs) and implement deref as calling your function 🙂

seancorfield03:09:18

That's what ya get for asking cljs questions in the #clojure channel I guess :rolling_on_the_floor_laughing:

idiomancy03:09:37

guilty as charged

mfikes03:09:42

We are all confused now 🙂

😅 4
seantempesta03:09:15

Anyone know how this is even possible? if-let is continuing to evaluate even though the result is null. I can repro the error in Clojure 1.9 and 1.10.0-alpha8.

seantempesta03:09:59

Not to mention how confusing this has been to debug since the exception is ArityException. I assume there is no valid arity for nil functions and it’s also referencing the wrong function name. :man-facepalming:

Alex Miller (Clojure team)03:09:27

trying to invoke nil should throw IllegalArgumentException, not ArityException. I’m wondering if what you’re seeing is not actually what’s happening due to locals clearing

Alex Miller (Clojure team)04:09:25

if you look at the detailMessage on the right pane, I think that’s where the real ArityException is being thrown - inside an anonymous function, inside kidlink-server.specs.datomic$fn__2484$fn__2485

Alex Miller (Clojure team)04:09:46

it would be interesting to open the stackTrace on the right pane there - might not be the same as what you’re seeing on the left

deliciousowl07:09:49

There used to be an Android REPL. Is there still such a thing?

Eric Ervin22:09:18

I have one running on my phone made by Daniel Solano Gómez. I can't remember when I downloaded it and can't say why it is no longer on Google Play #useless

Eric Ervin22:09:59

the app is useful. I'm useless

seantempesta09:09:01

@alexmiller: The stacktrace on the right pane was the same as the left and pointed to the same lines of code. You were correct though that the error was in my spec file. I just replaced anonymous functions until I found it. Thanks for your help debugging my issue! 🙂

chouser14:09:16

We just found some exciting non-determinism. Haven't tracked it all down yet, but we can reproduce it.

chouser14:09:30

i=0; while clj -Sdeps '{:deps {org.clojure/clojure {:mvn/version "1.10.0-alpha8"}}}' -e "(let [^Object e (java.util.concurrent.Executors/newSingleThreadExecutor), v (.get (.submit e #(vector :ok)))] (prn $i v) (.shutdown e) (assert (= [:ok] v)))"; do i=$(( i + 1 )); done

chouser14:09:20

That's a little bash loop starting up a new JVM per iteration. I've seen it succeed as many as 17 times in a row, but it usually has failed sooner than that.

chouser14:09:46

I'd be curious if anyone else sees it fail more or less consistently than that.

bostonaholic14:09:00

I can’t take credit for finding this, is this intentional?

user=> (defn foo [& {:keys [x y z] :or {x 1} :as params}]
  #_=>   (println "x = " x)
  #_=>   (println "params = " params))
#'user/foo
user=> (foo :y 2 :z 3)
x =  1
params =  {:y 2, :z 3}

bostonaholic14:09:19

the :or doesn’t apply to the :as

Alex Miller (Clojure team)15:09:37

this is the expected result. :as is always the original passed values (here represented as a map)

Alex Miller (Clojure team)15:09:50

:or is about making local bindings

bostonaholic15:09:39

that’s about what I figured, but it surprised me (and others)

chouser14:09:49

If the compiler knows the type of e at compile time, you get an error because it can't resolve .submit. The problem is that when the compiler doesn't know the type of e (because we "unhinted" it with ^Object), it defers method resolution to runtime, at which point it picks one of the .submit methods in an apparently non-deterministic way. Sometimes it picks the ^Callable which will return a value. Sometimes it picks ^Runnable which returns nil.

donaldball15:09:30

FWIW I have run into this very sharp edge before in production with a third party object that implemented both Callable and Runnable, but with different (!) behaviors.

ghadi16:09:18

also been burned

ghadi16:09:39

@chouser what JVM version?

chouser16:09:45

@ghadi Good question? I'm seeing nice quick failures on openjdk version "1.8.0_181"

ghadi16:09:41

k I'm trying openjdk 10

chouser16:09:47

I meant "Good question!!"

ghadi16:09:17

near immediate failure on 10

chouser16:09:57

I haven't looked at the reflection code t see where the indeterminism is coming from.

chouser16:09:00

Dunno if it's related to JVM version or not. We did test in one environment that had hundreds of successes before we gave up. I believe that was openjdk version "1.8.0_181" as well.

ghadi16:09:31

Class::getMethods doesn't promise order

ghadi16:09:35

But i'm surprised at non-determinism...

chouser14:09:05

For a given run of the JVM, it seems to keep picking the same one. Haven't tracked down exactly how it picks, why it sometimes picks the same one, or what has to be done again to have it pick another.

chouser15:09:20

@alexmiller BTW, thanks for clj -- made testing this a snap

Alex Miller (Clojure team)15:09:36

there is nondeterminism in the reflector’s choice of matching method if there are multiple possible matches

Alex Miller (Clojure team)15:09:56

to remove nondeterminism, don’t use reflection

chouser15:09:37

Right. But the default behavior is silent non-determinism. This seems bad to me. Has the possibility of having the default be deterministic (such as a runtime error similar to the compiler-time error) been discussed somewhere?

Alex Miller (Clojure team)15:09:35

possibly, but I couldn’t point you to it

Alex Miller (Clojure team)15:09:21

I agree that a deterministic answer would be better (whether it failed or succeeded)

Alex Miller (Clojure team)15:09:18

you could for example build a string of the type names in the params and sort by that, or something like that

Alex Miller (Clojure team)15:09:36

I’m actually in the process of looking at some of this stuff anyways in the context of CLJ-2066 (as this can also result in accidentally calling module-private methods rather than public interface methods)

chouser15:09:26

@alexmiller Ok. I found CLJ-792 as well -- after a quick read of the patch, it's not obvious to me if this would solve the problem, and I haven't gotten the patch to apply and pass Clojure regression tests yet. I wonder if that patch is just too ambitious to ever really have a chance of being included.

Alex Miller (Clojure team)15:09:27

oh yeah, that’s an oldie

Alex Miller (Clojure team)15:09:20

I remember when this was last worked on and I did review it a bit. Conceptually, I think it’s a good idea as this logic is tricky and at least partially duplicated.

Alex Miller (Clojure team)15:09:37

it’s likely work on CLJ-2066 will break this patch again even if you got it working

chouser16:09:18

@alexmiller Ok, I won't attempt it then.

ghadi17:09:32

i=0; while clojure -Sdeps '{:deps {org.clojure/clojure {:mvn/version "1.10.0-alpha8"}}}' -e '(require (quote clojure.pprint))' -e "(let [^Class fc (class #())] (clojure.pprint/pprint (vec (filter #(zero? (.getParameterCount %)) (.getMethods fc)))))"; do echo '---';  i=$(( i + 1 )); done
@chouser @alexmiller Class::getMethods is non-deterministic

ghadi17:09:00

you'll see the ordering twiddle occasionally

😱 4
chouser17:09:10

@ghadi ugh. nice find.

ghadi17:09:21

well it might be even non-deterministic within the same JVM -- there's a SoftReference cache under the covers

ghadi17:09:42

which would be a true wtf

micahasmith17:09:58

anyone have any advice on how to do an update-in-where? maybe where an element in the vec is a pred to find the next thing instead of a key or integer?

micahasmith18:09:13

just gonna keep-indexed for now.

dangercoder18:09:13

Anyone knows any open source web-based system in Clojure? I need some inspiration

jeff.terrell13:09:09

It's been a while, but I remember CircleCI had their web app open on Github and it was something of a model for others to follow. Not sure what the current state of things is there though.

restenb19:09:53

anybody got a nice way of sorting a sequence of maps by the value of a key in each map?

restenb19:09:02

i should say, the order is specific and predetermined by the value of that key in each map

andy.fingerhut19:09:06

Is the value of that key in each map a type like an integer, floating point, or string that Clojure's compare function sorts the way you like?

andy.fingerhut19:09:31

If so, as hiredman suggested, (sort-by :key-to-sort-on my-sequence) should do it.

andy.fingerhut19:09:27

If the values associated with that key need additional custom logic for sorting in your desired order, then you may want to write a custom comparator function, too, and/or use a function to transform the value to something else that compare already sorts the way you like.

restenb19:09:48

basically there's a list of values to sort by that's value-specific

restenb19:09:10

and a sequence of maps to sort according to that list of values, by a fixed key that exists in each map

hiredman19:09:40

so make a map that maps each value to its order, then use that map with sort-by

restenb19:09:22

something like this (def sort-order ["b" "c" "f"])

hiredman19:09:40

user=> (def m {:w 1 :y 2})
#'user/m
user=> (sort-by (comp m :k) [{:k :y} {:k :w}])
({:k :w} {:k :y})
user=>

andy.fingerhut19:09:59

You can create a map like this: (def sort-rank {"b" 1, "c" 2, "f" 3}).

restenb19:09:21

to sort (def unsorted-stuff [{:sort-key "c"}{:sort-key "f"}[:sort-key "b"}]

andy.fingerhut19:09:51

Then if your sequence of maps has a key :my-sort-key, and the value associated with that key is one of "b" "c" or "f", then you can use (sort-by #(sort-rank (:my-sort-key %)) my-sequence)

andy.fingerhut19:09:18

The first arg to sort-by there is a little function that first gets the value associated with the key :my-sort-key, and then it looks up that value in the map sort-rank, getting one of the number 1, 2, or 3 in my example def above, and then the elements of your sequence will be sorted via Clojure's compare function on those values 1, 2, or 3

andy.fingerhut19:09:23

which will be from smallest to largest

andy.fingerhut19:09:42

sort-rank need not be specified as a map -- I gave it that way in the example because it makes determining the sort rank a quick operation for each element in your sequence (i.e. nearly constant time, vs. a linear time scan through a list or vector)

andy.fingerhut19:09:12

And if I'd actually looked at what hiredman wrote, I would have noticed I was being redundant 🙂 My #(sort-rank (:my-sort-key %)) is equivalent to his shorter (comp sort-rank :my-sort-key)

restenb20:09:56

hm. weird. that almost worked ...

restenb20:09:15

except the two first values in the sorted sequence are not in the correct place

restenb20:09:42

might this be sensitive to length differences in sort-rank and my-sequence?

restenb20:09:32

anyway, i need to test some more. thanks to both!

andy.fingerhut20:09:55

sort and sort-by are 'stable' for any two elements that have the same comparison key. That might not be the case you are seeing, though. It shouldn't depend upon the lengths of sort-rank and my-sequence.

andy.fingerhut20:09:50

If you can boil it down to a small example you can share, someone could take a look at it to see if they can understand what is going on, but I understand if boiling it down to a small example can be too time-consuming.

lilactown21:09:20

I'd like to use https://github.com/oliyh/lacinia-gen to do generative testing of an app that consumes a GraphQL (Lacinia) API. However, I'm trying to figure out how I could further constrain the values so that I can test specific use cases

idiomancy22:09:41

Hey, so... what does cljc stand for? Clojure Core? Clojure Code? Clojure Conditional?

idiomancy22:09:31

I believe you right, but how do those links support that?

pauld23:09:16

I would have thought it stood for 'common' as in code that is common to clojure and clojurescript.

Alex Miller (Clojure team)23:09:17

it stands for Common

😀 4
Alex Miller (Clojure team)23:09:23

according to Rich, who I will consider the authoritative source :)

awb9923:09:46

I have a somehow stupid question: I wonder why many clojure functions are not designed in a way to have a "mappable parameter" in the end: (str/split "Clojure is awesome!" #" ")

awb9923:09:08

So in this case, the str/split last parameter in my view should not be the separator, but the string that should be separated,

awb9923:09:28

because this I would typically use to map over when I want to split a list of strings.

awb9923:09:00

(str/split #" " "Clojure is awesome!") woudl be a more sensible design in my view.

Alex Miller (Clojure team)23:09:14

(doc clojure.string) actually describes the rationale

Alex Miller (Clojure team)23:09:54

str/join is an exception (and I think str/split could have used a similar rationale) to put the partial-able arg first

Alex Miller (Clojure team)23:09:26

I don’t know but suspect it’s not there b/c of the optional limit arg which gets a little weird

awb9923:09:08

@alexmiller Glad you see it also as an exception. I somehow managed to use lots of functions like this example.

awb9923:09:16

Perhaps just coincidence.

awb9923:09:45

I see a special use for a new kind of macro

awb9923:09:50

The ->> macro

awb9923:09:27

should be written, that if inside of its code tree it finds a operator,

awb9923:09:52

then it will not send the data to the last position but to the position where the operator applies.

awb9923:09:28

This is the first sensible use of macros that I discovered 🙂

noisesmith23:09:37

this is close to what as-> does, but as-> is simpler

awb9923:09:13

I am not coming any further with the docs on as-> operator. So say I build a pipeline with -> and then I have some functions where I need to "fill in" a parameter that is not the last one...

awb9923:09:25

would I use the as-> only applied to this one function?

noisesmith23:09:40

Exactly, (-> x (foo) (as-> $ (bar $ $)) (baz))

awb9923:09:46

(map #(str/split % #"/"))

awb9900:09:08

I could not get it to work with ->

awb9900:09:00

In my -> workflow I have this map string splitting somewhere in the middle.

Alex Miller (Clojure team)00:09:27

Thread macros do pure syntactic rearrangement. In the case of an anonymous function that means putting the result of the prior into the list defining the anonymous function. If you macroexpand the form, you’ll see why this won’t work

misha05:09:24

if threading form is not that tall, I go with as-> from the beginning:

(as-> x $
  (foo $)
  (bar $ $)
  (baz $))
is there any reason it is worse than your example? @noisesmith

noisesmith17:09:23

@U051HUZLD that's a subjective decision, to my eye using the placeholder in every clause introduces a lot of unneeded clutter

noisesmith17:09:36

as-> was designed to be usable inside ->

awb9905:09:28

I payed around with a few examples, and I also find it easier to use as-> as a replacement of -> or ->> really. So in cases that the functions that I want to call have different patterns, then using as-> as a sort of more general form seems to work fine

Cameron23:09:50

I've wondered about this for a while too; when I first came to Clojure, I was coming from Haskell and I was currying my functions and composing function transformations on data by .. well, directly using comp (so as an example, ((comp (add 1) (sub 2) (mult 3)) 12) instead of (-> 12 (+ 1) (- 2) (* 3)), where add, sub, and mult are assumed to be curried). Then I discovered -> and ->>, but also discovered that some things seemed to be fit for the comp / ->> way, and others for the -> way, so I've wondered about that myself (the example being drop is (drop n coll) and composes with other functions with ->>, but split is (split string delimiter), and composes with ->.)

noisesmith23:09:34

->> can be used inside ->, in fact all the "arrow macros" can be used inside ->

noisesmith23:09:05

the thing to be careful of is that these macros are not semantic operations, they are a way of folding and unwrapping source forms eg. this next example is not an analog to currying or method chaining:

user=> (->> (+ a b) (let [a 31 b 11]))
42

awb9923:09:13

You lost me in this example.

awb9923:09:18

How does that work.

awb9923:09:28

The variables a and b are local only ?

awb9923:09:15

And (+ a b) should be evaluated first.

noisesmith23:09:08

The point is that ->> rewrites code before compiling, that's all it does

awb9923:09:26

I get that. This is what a macro does, and it makes sense, because otherwise the whole code would not be valid clojure syntax.

awb9923:09:41

But I thought it evaluates from first to last expression,

awb9923:09:55

and I thought that in your exampel a and b are out of scope,

sundarj23:09:05

no - macros don't evaluate any of their arguments, and thus can control how they get evaluated arbitrarily. in the case of ->>, it takes the arguments (+ a b) and (let [a 31 b 11]), then - at compile time - rewrites them into (let [a 31 b 11] (+ a b)) which is then what eventually gets evaluated

awb9923:09:08

and should be evaluated After + was evaluated.

sundarj23:09:04

to the ->> macro, (+ a b) is simply a list of three symbols, +, a, and b

awb9923:09:11

(defn get-data [mySymbol] (->> (slurp (str "resources/CSV/" mySymbol ".csv")) (csv/read-csv) (drop 1) ; drop header row (map parse-row-)))

Cameron23:09:20

Only after ->> has finished, and the new expression has been created, is anything evaluated. Until then it goes from (+ a b) unevaluated, to (let [a 31 b 11] (+ a b)) unevaluated, and so on.

awb9923:09:23

I used this function to load a DSV file and process it.

awb9923:09:41

And first it slurps, then it processes csv , then it drops headers, then it parses the row data.

awb9923:09:50

your example should execute the other way around.

awb9923:09:54

this is what confuses me.

awb9923:09:15

I evaled your example, and it works.

awb9923:09:23

but I am confused why.

the2bears23:09:58

(-> 5 f g) is going to look like (g (f 5))

the2bears23:09:10

if g is a let binding...

the2bears23:09:41

(let [x 4] (f 5))

the2bears23:09:07

if f then is something that uses 'x' it'll be bound

the2bears23:09:31

yeah, it's a bit tricky

awb9923:09:02

so in a way,

awb9923:09:19

I can used it to partially apply parameters inside the functions.

awb9923:09:47

that sort of blows up my head .. hahah

😁 4
andy.fingerhut01:09:31

I would add that if your goal is to write code that is clear to others, I would not hold up (->> (+ a b) (let [a 31 b 11])) as a shining example of clarity to humans.

andy.fingerhut01:09:38

I would almost go so far as to say that someone writing code like that might be trying to pull a fast one on you.

misha05:09:24

if threading form is not that tall, I go with as-> from the beginning:

(as-> x $
  (foo $)
  (bar $ $)
  (baz $))
is there any reason it is worse than your example? @noisesmith