Fork me on GitHub
#clojure
<
2017-11-30
>
qqq00:11:23

agreed, @noisesmith is one of the most helpful people here; we should start tipping him in micro bitcoins or something 🙂

Ryan Radomski02:11:58

Does anybody have any Emacs configs to expand upon this? http://fgiasson.com/blog/index.php/2016/06/14/my-optimal-gnu-emacs-settings-for-developing-clojure-revised/ I adopted this config when I started just under a year ago and I never thought about it since

gonewest81804:11:11

I’ve switched to the zenburn theme because I find it easier on my eyes than monokai. I configured erc-mode because the leiningen developers hang out on IRC, and I have a pull request pending there. I’ve recently adopted eshell and added some config tweaks I needed, including a custom prompt for virtualenvwrapper. Up until very recently I used exclusively melpa-stable, but then I released a (silly) elisp package called adafruit-wisdom to melpa… As a result, I needed to figure out how to pin packages to a specific repository, that’s the stuff at the very top of my init.el. Take a look, maybe some of this will appeal to you too. https://github.com/gonewest818/.emacs.d

igrishaev06:11:07

@radomski you may take a look at my emacs config that I’ve been using for 5 years: https://github.com/igrishaev/dotfiles/blob/master/.emacs

rauh07:11:54

How come coll-reduce isn't implemented for Iterator?

qqq07:11:41

1. I'm not using datomic. 2. I need to inspect a large eav store (about 10,000 items). 3. Anyone knows of a good tool for analyzing this?

Empperi08:11:13

10000 items isn’t exactly large

Empperi08:11:58

I think you can just load that into memory and use Datascript

qqq09:11:22

no no, I meant I'm currently dumping it via println

qqq09:11:29

and manually inspecting it by hand when something goes wrong

h.elmougy11:11:06

(s/def ::delay (s/and (s/or :val pos? :val zero?) int?))

h.elmougy11:11:07

(s/valid? ::delay 0)

h.elmougy11:11:58

if I restructure the ::delay to (s/def ::delay (s/and int? (s/or :val pos? :val zero?)))

h.elmougy11:11:02

it then works

h.elmougy11:11:13

can anyone explain why

h.elmougy11:11:09

(s/def ::delay (s/and (s/or :val pos? :val zero?) int?)) (s/valid? ::delay 0) => false (s/def ::delay (s/and int? (s/or :val pos? :val zero?))) (s/valid? ::delay 0) => true

andre.stylianos11:11:42

you can use s/explain in place of s/valid? to get more information

andre.stylianos11:11:21

I tried it here and it seems like the first option passes [:val 0] to the int? pred

andre.stylianos11:11:05

so this is probably what's causing it to fail

h.elmougy11:11:23

is it normal behavior?

h.elmougy11:11:36

i think it shouldn't

andre.stylianos11:11:25

Seems that's the case as (spec/def ::delay (spec/and (spec/or :val pos? :val zero?) #(int? (second %)))) works

p-himik11:11:28

https://clojuredocs.org/clojure.spec/or "Returns a destructuring spec that returns a map entry containing the key of the first matching pred and the corresponding value."

andre.stylianos11:11:00

and that I don't know enough to say

andre.stylianos11:11:03

I thought that the above would only be true when conforming, didn't realize it would have this kind of implication

andre.stylianos11:11:32

btw @h.elmougy you can use pos-int? as well

p-himik11:11:58

I think it makes sense. This way, you can check only specific keys in the following specs.

laujensen14:11:27

I have something like this (reduce + (map #(* (first %) (last %)) …) that runs at 1500msecs. Then I tried it in a loop and it drops to 20msecs. Why does Rich hickey hate me so ?

p-himik14:11:56

How did you do it in a loop?

laujensen14:11:24

(loop [acc 0 item items] (if (seq item) (recur (+ acc (….))) acc)

laujensen14:11:26

something like that

p-himik14:11:52

Still not clear. 🙂 It depends on what items is and how you recur on item.

p-himik15:11:42

In CLJS and items being a vector, reduce works about 8% faster than loop:

(def items (mapv #(vector % %) (range 10000)))
(simple-benchmark [] (reduce + (map #(* (first %) (last %)) items)) 1000)
[], (reduce + (map (fn* [p1__115369#] (* (first p1__115369#) (last p1__115369#))) items)), 1000 runs, 4812 msecs
(simple-benchmark [] (loop [acc 0, items items] (if (seq items) (recur (let [i (first items)] (+ acc (* (first i) (last i)))) (rest items)) acc)) 1000)
[], (loop [acc 0 items items] (if (seq items) (recur (let [i (first items)] (+ acc (* (first i) (last i)))) (rest items)) acc)), 1000 runs, 5219 msecs

mgrbyte15:11:13

I wonder what time you get with first and peek (instead of last) in the map version

p-himik15:11:09

(simple-benchmark [] (reduce + (map #(* (first %) (peek %)) items)) 1000)
[], (reduce + (map (fn* [p1__118806#] (* (first p1__118806#) (peek p1__118806#))) items)), 1000 runs, 2304 msecs

mogenslund15:11:42

Just a suggestion. Is it because map creates a new list, before reducing on it? I wonder how this would perform: (reduce #(+ (* (first %2) (last %2)) %1) 0 ....) Skipping the creation of the intermediate list.

p-himik15:11:26

There's no an intermediate list. map is lazy.

rauh15:11:26

@p-himik Use destructing instead of first/last. Last isn't all that fast and will create another intermediate sequence actually.

p-himik15:11:09

@rauh I know, I was deliberately repeating what laujensen provided.

rauh15:11:21

Oh I see! 🙂

p-himik15:11:07

By the way, first with peek is faster than destructuring by about 15%.

bronsa15:11:59

you can't destructure last

dpsutton15:11:37

items are a two element vector so you can in this instance but certainly not in the general sense of last

bronsa15:11:45

@laujensen also make sure your first reduce isn't actually realizing the lazy seq vs your loop using the already realized one

bronsa15:11:36

there's just way too much you haven't said about this to make a guess, but there's absolutaly no way that a 1:1 translation from reduce to loop results in 2 factors of magnitude perf increase

bronsa15:11:45

in fact, often reduce is faster than manual looping over a seq view

laujensen16:11:47

Thanks for the input guys, I'll fiddle a bit

plins16:11:06

hello everyone, im coding a scheduler which receives a function as a parameter, if the function execution raises an exception, id like to log the function name is there a standart way of getting a function name in clojure?

laujensen16:11:15

(-> func meta :name) ?

bronsa16:11:21

that works if you pass a Var, not if you pass a function as a value

timrichardt16:11:51

;; get name of the created class for foo
((fn [f]
   (-> f
       .getClass
       .getName)) clojure.string/replace) => clojure.string$replace

timrichardt16:11:58

It's a bit hacky.

timrichardt16:11:15

And expensive I believe, because you do a reflection.

bronsa16:11:02

not all "reflection" is slow, this is not the case. but it's still a bad idea :)

timrichardt16:11:25

user> (bench ((fn [f]
                (-> f
                    .getClass
                    .getName)) clojure.string/replace))
Evaluation count : 10640280 in 60 samples of 177338 calls.
             Execution time mean : 5.649725 µs
    Execution time std-deviation : 123.414594 ns
   Execution time lower quantile : 5.453163 µs ( 2.5%)
   Execution time upper quantile : 5.886103 µs (97.5%)
                   Overhead used : 8.291648 ns

Found 1 outliers in 60 samples (1.6667 %)
	low-severe	 1 (1.6667 %)
 Variance from outliers : 9.4543 % Variance is slightly inflated by outliers
nil

timrichardt16:11:57

5µs, depending on the use case, can be a long time, i agree

bronsa16:11:19

well, there's 2 instances of reflection in this code, one is avoidable and it's the slow reflection, the other is .getClass itself which is fast

bronsa16:11:34

just rebench with (fn [^Object f] and you'll see what I mean

timrichardt16:11:13

user> (bench ((fn [^Object f]
                (-> f
                    .getClass
                    .getName)) clojure.string/replace))
Evaluation count : 5075442000 in 60 samples of 84590700 calls.
             Execution time mean : 4.068115 ns
    Execution time std-deviation : 0.410098 ns
   Execution time lower quantile : 3.483281 ns ( 2.5%)
   Execution time upper quantile : 4.942832 ns (97.5%)
                   Overhead used : 8.291648 ns

Found 2 outliers in 60 samples (3.3333 %)
	low-severe	 2 (3.3333 %)
 Variance from outliers : 70.3505 % Variance is severely inflated by outliers

bronsa16:11:38

right, and that's essentially free

timrichardt16:11:44

4ns? What happened? 😳

bronsa16:11:00

you got rid of the avoidable reflection :)

timrichardt16:11:14

I have to read about this reflection thing. Thank you!

bronsa16:11:58

this is what in clojure we refer as reflection, but generally speaking my point was that in java lang, Object.getClass is technically reflection too

bronsa16:11:21

but when we talk about reflection in clojure, it's in the context of reflective calls at runtime which is a very narrow and specific instance of reflection

bronsa16:11:34

which is what your function was also doing, but unnecessarily

timrichardt16:11:13

Do you have an example where you cannot avoid reflection?

timrichardt16:11:22

Reflection in the wider sense.

bronsa16:11:06

if you want to do stuff like (deftype Foo [x]) (deftype Bar [x]) (defn foo [a] (.-x a)) then that needs to be reflective

bronsa16:11:18

but generally speaking reflection is avaoidable in 90% of the cases

plins17:11:15

@U060FKQPN this means i should typehint my parameteres when possible? only functions? is there a way in which I can clojure.specstuff and get type hints for free?

bronsa17:11:39

no don't start type hinting everything

bronsa17:11:09

my suggestion is to (set! *warn-on-reflection* true) in your project at dev time and just type hints the warnings away

bronsa17:11:27

there's no way to get type hints from clojure.spec, type hints are compile time and spec is runtime

plins17:11:55

thanks 🙂

noisesmith17:11:07

addendum about warn-on-reflection - if you use lein, lein check will show all your reflection warnings

plins17:11:56

maybe cursive may warn me about those with a little config?

mogenslund16:11:13

(-> func var meta :name)

bronsa16:11:39

that's not how var works

kwladyka19:11:45

What do you use for data validation? Clojure spec looks great to generate and check data validation, but on the other hand it is totally not human readable. So using clojure spec i would have to validate twice, once with clojure spec and once for users with usability messages. It sound like something is wrong in this pattern. How do you do this? I would like to use clojure spec but something deep inside me saying i need complete solution to generate, validate, human readable information and spec doesn’t fit into this at that moment.

noisesmith19:11:27

@kwladyka there are libraries for mapping from the spec errors to human readable messages, the intention for spec is to be easy to integrate with tooling for readability

seancorfield19:11:39

@kwladyka We use just spec -- and then explain-data for failures, and we map the data structure to our own custom error messages.

kwladyka19:11:19

Do you have some public example?

seancorfield19:11:32

No, sorry. It's code at work.

dpsutton20:11:21

has anyone written a let style macro that puts the bindings at the end? I do like the haskell where syntax whereby the local definitions can follow after usage

laujensen20:11:51

dpsutton, does that have some benefit im not understanding?

dpsutton20:11:57

just for clarity of reading. For example you define a function and have a bunch of stuff in the let bindings and there's a lot of details immediately and then the implementation. The where style let's you put some clear logic and then define the local bindings after their usage. A macro could simple rewrite this into a traditional let but i do like the usage first

hiredman20:11:38

I did something like that in my raft implementation

dpsutton20:11:41

@laujensen and example is the following:

levelorder :: (Ord a) => Tree a -> [a]
levelorder t = step [t]
	where
		step [] = []
		step ts = concatMap elements ts ++ step (concatMap subtrees ts)
		elements Nil = []
		elements (Node left x right) = [x]
		subtrees Nil = []
		subtrees (Node left x right) = [left,right]

dpsutton20:11:59

with good naming it can make the logic really clear and push the details down below but still close if you are interested

hiredman20:11:12

it has these things I called "rules" that have three parts, a true/false expression that determines if the body should be applied, a body that updates the passed in state, and then bindings (usually a big destructuring) that establish any bindings for the first two parts

dpsutton20:11:25

in clojure, the let puts all of the stuff above. i can see both having their benefits but sometimes just want to see a nice clear logic

laujensen20:11:49

@dpsutton I think Ive done too much Clojure and too little Haskell to find that order help. Looks upside down to me 🙂

dpsutton20:11:55

that's the point

bfabry20:11:41

one thing with the let ordering is that it's the same dependency order as defn/def ordering, so there's some consistency here. also with imperative language statement ordering

dpsutton20:11:26

yeah i'm thinking some syntax like

(with-delayed-context
  (-> (sanitize-data data)
    (filter incomplete?)
    (map complete-names))
  (with-bindings [sanitize-data (fn [data] ...)
                  incomplete? (fn [item] (and ...))
                  complete-names (fn [item] (or ...))]))

dpsutton20:11:43

put the high level logic at the top and the details below

dpsutton20:11:02

but better names than with-delayed-context and with-bindings

hiredman20:11:03

(defn where [body bindings] `(let ~bindings ~body))

dpsutton20:11:32

but you lose the implicit do i believe. that's why i was thinking a keyword to signal the end of the body

dpsutton20:11:52

so keep taking forms as the body until you see the with-bindings or where and then invert

hiredman20:11:54

or just use do if you need one

noisesmith20:11:41

(defmacro where
  [& body-and-bindings]
  `(let ~(last body-and-bindings)
     ~@(butlast body-and-bindings)))
user=> (where (+ x y) [y 1 x (inc y)])
3
?

dpsutton20:11:43

ah, don't overcomplicate it. nice

tmountain20:11:53

could anyone shed some light on why this doesn't work?

(mapcat :id
  [[{:id 1} {:id 2}]
   [{:id 3} {:id 4}]])

tmountain20:11:32

this works fine

tmountain20:11:35

(map :id [{:id 1} {:id 2} {:id 3} {:id 4}])

noisesmith20:11:02

@tmountain [{:a 0}] doesn’t have an :a key

noisesmith20:11:12

it’s a vector containing an item with an :a key

noisesmith20:11:28

mapcat unfolds after, not before, the input list

noisesmith20:11:39

you could do (map :id (apply concat coll)) instead

noisesmith20:11:09

or (into [] (comp cat (map :id)) coll)

tmountain20:11:23

awesome, thanks!