#clojure log - Nov 01 2014

The Joy of Clojure
Main Clojure site
Google Group
IRC
List of all logged dates

0:04 mercwithamouth: ping

0:05 justin_smith: $ping

0:05 lazybot: justin_smith: Ping completed in 0 seconds.

0:10 Raynes: $ping google.com

0:10 lazybot: Raynes: FAILURE!

0:10 Raynes: Oh no

0:10 The googles is unpingable

0:11 justin_smith: Raynes: so I got far enough with lazybot to break everything with the new lib version, and then far enough to make it all compile nicely again and connect - now for things like joining channels and responding to them!

0:11 $google teh googles

0:11 lazybot: [Teh Google | Mother Jones] http://www.motherjones.com/kevin-drum/2009/02/teh-google

0:12 Raynes: justin_smith: Yay!

1:26 zspencer: So, I'm pretty sure I just re-wrote a built in clojure function I'm unaware of

1:27 pdk: k what does it do

1:27 zspencer: https://gist.github.com/zspencer/c3e8baeec900858fd053

1:27 I'm trying to explain it in words

1:27 and I can't

1:27 other than "It conversts a map of vectors to a vector of maps"

1:27 which isn't a good description

1:28 is there a word for this?

1:29 pdk: that use case seems specialized enough that i'd be surprised if it was builtin honestly

1:29 zspencer: Hmm

1:29 I figured this would be pretty common

1:31 justin_smith: zspencer: woah, those names

1:31 zspencer: When I can't think of a good name, I just type until my fingers bleed

1:31 ;)

1:32 justin_smith: ,(apply merge-with #(conj [] %1 %2) [{:a 1 :b 2} {:a 3 :b 4}])

1:32 clojurebot: {:b [2 4], :a [1 3]}

1:33 justin_smith: zspencer: that's what you want, right?

1:33 oh duy (facepalm)

1:33 zspencer: yes

1:33 justin_smith: ,(apply merge-with vector [{:a 1 :b 2} {:a 3 :b 4}])

1:33 clojurebot: {:b [2 4], :a [1 3]}

1:33 justin_smith: much better

1:34 pdk: modern miracles

1:34 justin_smith: zspencer: the one thing that sucks about that is the behavior for non overlapping keys

1:34 ,(apply merge-with vector [{:a 1 :b 2 :c 3} {:a 3 :b 4 :d 4} {:a 5}])

1:34 clojurebot: {:d 4, :c 3, :b [2 4], :a [[1 3] 5]}

1:34 justin_smith: oh ick!

1:34 and that nesting thing...

1:39 amalloy: justin_smith: it's hard to imagine merge-with vector ever being a good plan. better to start from {:a [1] ...} and use merge-with into

1:39 justin_smith: amalloy: yeah, working an a merge-with / into solution right now

1:39 zspencer: yea my starting vector could be many entries

1:41 amalloy: (reduce (fn [acc m] (reduce (fn [m [k v]] (if (contains? m k) (update-in m [k] into v) (assoc m k [v]))) acc m) {} vector-of-maps)?

1:42 justin_smith: ,((fn [& ms] (let [base (zipmap (set (mapcat keys ms)) (repeat []))] (reduce #(merge-with conj % %2) base ms))) {:a 1 :b 2 :c 3} {:a 3 :b 4 :d 4} {:a 5})

1:42 clojurebot: {:a [1 3 5], :d [4], :b [2 4], :c [3]}

1:43 zspencer: that seems a bit harder to follow than my solution :/

1:43 much terser

1:44 amalloy: ,((fn [vector-of-maps] (reduce (fn [acc m] (reduce (fn [m [k v]] (if (contains? m k) (update-in m [k] conj v) (assoc m k [v]))) acc m)) {} vector-of-maps)) [{:a 1 :b 2} {:a 3 :b 4}]) is another option

1:44 clojurebot: {:a [1 3], :b [2 4]}

1:44 justin_smith: zspencer: one difference though is it comtains exactly two names I wouldn't know already based on my knowledge of clojure

1:44 amalloy: does less preprocessing than justin_smith's, but is probably harder to follow

1:46 zspencer: so you create a set of all the keys from the map

1:46 justin_smith: zspencer: all the keys from all the maps

1:46 zspencer: then you reduce the maps onto that set ofkeys

1:46 using merge-with conj

1:46 justin_smith: (inc case they have different keys)

1:46 lazybot: ⇒ 1

1:46 justin_smith: right

1:47 zspencer: yes all keys from all maps

1:47 justin_smith: actually there was some silliness in there

1:47 ,((fn [& ms] (let [base (zipmap (set (mapcat keys ms)) (repeat []))] (apply merge-with conj base ms))) {:a 1 :b 2 :c 3} {:a 3 :b 4 :d 4} {:a 5})

1:47 clojurebot: {:a [1 3 5], :d [4], :b [2 4], :c [3]}

1:47 justin_smith: much better

1:48 since merge-with is varargs we can just use apply

1:48 zspencer: the (repeat []) sets each of the new keys to []

1:48 justin_smith: right, with the help of zipmap

1:48 ,(zipmap (range 10) (repeat []))

1:48 clojurebot: {0 [], 7 [], 1 [], 4 [], 6 [], ...}

1:49 justin_smith: in the newer version I realized that reduce merge-with is the same as apply merge-with (but noisier) and that #(conj % %2) is just conj when you know there are two arguments

1:50 ,((fn [& ms] (let [base (zipmap (mapcat keys ms) (repeat []))] (apply merge-with conj base ms))) {:a 1 :b 2 :c 3} {:a 3 :b 4 :d 4} {:a 5})

1:50 clojurebot: {:d [4], :a [1 3 5], :b [2 4], :c [3]}

1:50 justin_smith: the call to set was redundant too, since this is a hash-map

1:52 zspencer: yea, thought that it wouldn't really matter

1:52 well, I mean, theoretically it would reduce the amount oftimes repeat was called

1:52 justin_smith: technically it does some pointless conj calls I bet

1:52 right

1:52 the conj is more expensive

1:52 repeat just returns the same thing every time, way cheap :)

1:53 zspencer: it shouldn't do any extra conj calls

1:53 justin_smith: why wouldn't it?

1:53 $source zipmap

1:53 lazybot: zipmap is http://is.gd/p8ndwz

1:53 justin_smith: I mean conj inside zipmap

1:53 zspencer: oh wait, you mean zipmap insdie conj

1:53 gotcha

1:53 justin_smith: or assoc

1:53 yeah, I mean assoc (though conj would do the same thing)

1:54 but calling set has its own set of conj calls, so it's likely a near wash

1:55 zspencer: yeah

1:55 justin_smith: the faster version would use distinct I guess, but we are aiming for clarity at this point anyway

1:56 oh, distinct uses a set anyway, so never mind that

1:56 $source distinct

1:56 lazybot: distinct is http://is.gd/KRHiA5

1:57 zspencer: nice

1:57 thanks justin_smith

1:57 I've been out of clojure for ~6 months

1:57 justin_smith: np

1:57 zspencer: so all my skills are rusty

1:58 justin_smith: I try to keep my skills sharp by doing my best to help people with questions on #clojure

1:59 zspencer: well played ;)

1:59 Yea, I'll start idling in here again

1:59 justin_smith: it's true though!

1:59 zspencer: Just dropped my day job, so more free time to work on fun code

2:00 justin_smith: here's to lot's of the fun freedom, and little of the dismal pointlessness that can come with an unemployed spell, then

2:00 (past time for me to get another day job myself)

2:01 zspencer: yea, I've got freelancer work lined up

2:01 and a full time gig in a month

2:01 justin_smith: I've been freelancing, but with little reason to leave the house things are different

2:01 oh, cool

2:01 zspencer: just a temporary respite from 1 full time job and 3 freelancing gigs to 3 freelancing gigs :)

2:02 TEttinger: hang on, getting this for another channel

2:02 ~ask

2:02 clojurebot: The Ask To Ask protocol wastes more bandwidth than any version of the Ask protocol, so just ask your question.

2:04 pdk: maybe some of us enjoy irc 20 questions!!

2:04 justin_smith: pdk: is your question about something blue?

2:05 pdk: could be

2:05 justin_smith: that's not how one plays 20 questions at all!

2:05 pdk: it's a game about asking 20 questions

2:05 who said any of them got resolved?

2:06 zspencer: Sounds like code review

2:11 rritoch: Does clojure provide any function to unquote cquoted strings? (ex. "\\'\n" -> "'\n")

2:13 mindbender1: Good day all!

2:16 I need a function (condf-> (fn []) :true ["value"] :false nil :default :value)

2:16 TEttinger: rritoch, that example seems like just removing the first escaped backslash

2:17 mindbender1, is this for 4clojure?

2:17 rritoch: TEttinger: Clojure quotes newlines but not single quotes

2:17 ,"\'"

2:17 clojurebot: #<RuntimeException java.lang.RuntimeException: Unsupported escape character: \'>

2:17 amalloy: mindbender1: that request doesn't appear to make any sense at all. how do you envision this function being used? like, an actual compiling example

2:18 TEttinger: ,"\\'"

2:18 clojurebot: "\\'"

2:18 rritoch: ,"'\n"

2:18 clojurebot: "'\n"

2:19 rritoch: So I need (= (somefunc "\\'\n") "'\n")

2:21 Technically this should also be true (= (somefunc "\\'\\n") "'\n") if cslashes are unescaped properly

2:24 This is the closest I found but it doesn't handle \r\n\t properly

2:24 ,(clojure.string/replace "\\'\n" #"\\(.)" "$1")

2:24 clojurebot: "'\n"

2:25 TEttinger: ,(clojure.string/replace "\\'\\n" #"\\(.)" "$1")

2:25 clojurebot: "'n"

2:25 TEttinger: I see your problem

4:24 raspasov: hey guys does anyone have experience with CRTDs, specifically I'm curious if anyone has ever tried or seen an implementation of persistent (in the Clojure PersistentHashMap sense) CRDTs

5:36 rritoch: ,(let [cunescape (fn [s] (apply str (map #(-> %1 (clojure.string/replace "\\r" "\r") (clojure.string/replace "\\n" "\n") (clojure.string/replace "\\t" "\t") (clojure.string/replace #"\\(.)" "$1")) (re-seq #"\\.|[^\\]*" s))))] (cunescape "test\\r\\n\\t\\'"))

5:36 clojurebot: "test\r\n\t'"

5:37 rritoch: Any ideas on how to improve this? I thinkk I solved the unescape cslashes problem, but this code is a bit redundant with all of these replacements so it must be improvable

5:49 ,(let [cunescape (fn [s] (apply str (map #(reduce (fn [l [m r]] (clojure.string/replace l m r)) %1 [["\\r" "\r"] ["\\n" "\n"]["\\t" "\t"][#"\\(.)" "$1"]]) (re-seq #"\\.|[^\\]*" s))))] (cunescape "test\\r\\n\\t\\'"))

5:49 clojurebot: "test\r\n\t'"

5:50 rritoch: Thats about as clean as I can get it... Still ugly

6:14 SagiCZ1: could anyone explain this? i wanted to require some namespaces like this:

6:14 (ns this-ns (:require (prefix [other-ns :refer :all] [other-ns2 :refer :all] [prefix2.other-ns3 :refer :all])))

6:14 and i got this exception "Found lib name 'indicator.moving-average' containing period with prefix 'swarm'. lib names inside prefix lists must not contain periods"

6:18 i thought that if the ns3 is deeper than the other ns i can just add the necessary prefix

6:18 rritoch: SagiCZ1: Remove the parenthesis and wrap prefix with [] as [prefix]

6:19 (ns this-ns (:require [prefix] [other-ns :refer :all] ....))

6:20 SagiCZ1: rritoch: thanks but that tries to find a ns called prefix_init.class

6:21 rritoch: So your trying to load multiple namespaces into the same prefix?

6:21 SagiCZ1: i have multiple namespaces with the same prefix, i dont want to repeat the prefix for each

6:22 it works for one level nesting

6:23 i learned this from incanter, see thsi http://stackoverflow.com/a/22600355

6:24 rritoch: Ok, so where does this prefix2 come from?

6:26 SagiCZ1: lets say there is a namespace called incanter.prefix2.namespace ,how would i require it using the method above?

6:28 rritoch: Ok, I see. It probably works similarly to import, but testing that theory now.

6:33 Looks like you will need to separate them, (ns this-ns (:require (prefix [other-ns :refer :all] [other-ns2 :refer :all]) (prefix.prefix2 [.other-ns3 :refer :all]))

6:33 err: ns this-ns (:require (prefix [other-ns :refer :all] [other-ns2 :refer :all]) (prefix.prefix2 .other-ns3 :refer :all]))

6:34 I can't get this synergy to work correctly, I'm not working directly from the box with IRC on it... just remove the dot from .other-ns3 @ the end.

6:38 SagiCZ1: rritoch: ok, i guess i will keep them separated, thanks for the effort though

9:35 lazylambda: Hello everyone, can anyone here kindly explain in layman terms the difference between monads and transducers?

9:38 mearnsh: the cheese on top tastes better

9:39 lazylambda: i can't but this thread might help you? https://news.ycombinator.com/item?id=8344002

9:40 lazylambda: mearnsh: Thanks, I'll check that out

10:06 justin_smith: SagiCZ1: prefix require is discouraged, and no, it does not nest at all

10:07 mearnsh: a transducer is much less formally defined, and much less general, for one thing.

10:08 mearnsh: in a very vague way you could compare a transducer to a function that can be lifted into a monad. But the problem is that the point of monads is that any function can be lifted into them so...

10:08 mearnsh: i know about that much. the definition thing isn't really an important distinction though because you could produce a formal definition of a transducer

10:09 justin_smith: so maybe a context that can use a transducer can be compared to a weak kind of monad

10:09 mearnsh: lazylambda was asking but thanks

10:09 justin_smith: oh, oops

10:09 lazylambda: ^ respondend to the wrong person, sorry

10:11 SagiCZ1: justin_smith: good morning, is it discouraged? why is that so? i just copied it from incanter but i dont have to use it if its not good

10:12 justin_smith: SagiCZ1: I guess you could say it is a somewhat controversial feature

10:12 as you have learned it does not nest

10:12 the argument against it is that it makes it hard to use a searching tool like grep to find usage of a namespace

10:12 SagiCZ1: i see, that makes sense

10:13 justin_smith: I figured this out after transforming a bunch of namespaces to use this style...

10:13 lazylambda: justin_smith: thanks for the response.. so basically transducers are less powerful than monads?

10:14 justin_smith: lazylambda: yes, much more limited. Also I like bbloom's idea that we should aim to use the weakest construct we can for a given problem.

10:15 lazylambda: justin_smith: why?, for performance reasons?

10:16 justin_smith: not really - I think that's unrelated

10:16 for code complexity reasons

10:16 goto is a very powerful construct, and tends to perform very well

10:16 but it's usually better to use something weaker like if or case

10:17 lazylambda: justin_smith: I see, that makes sense. I read in Sedgewick's algorithms book that wide interfaces are usually a bad thing

10:17 justin_smith: I think that is a similar thing, yes

10:18 lazylambda: that said, I have definitely had situations where I wished we had an efficient and sugared monad in clojure

10:18 we have some libs with monads but they are a bit clunky and they are slow

10:18 lazylambda: justin_smith: What about algo.monad or whatever it's called

10:18 justin_smith: yeah

10:19 algo.monad doesn't have the kind of sugar and compiler support that haskell monads have

10:19 lazylambda: justin_smith: I've never used monads before, I don't even understand them very well yet. I only know the very basic haskell stuff, but I am planning to go deeper

10:19 justin_smith: lazylambda: I think there are a lot of us in that boat

10:20 I keep trying, but the abstractions haven't fully sunk in

10:20 transducers are a different kind of thing entirely - they mostly exist for efficiency reasons

10:20 lazylambda: justin_smith: will monads be useful in clojure though? my friend who is more experienced in haskell tells me that monads are weird without a type system. Is that true?

10:21 justin_smith: to the degree that they are a coding convenience, it is mostly for someone implementing something like core.async where you want an efficent version of map, filter, reduce, etc. for your context

10:21 lazylambda: many haskell folks say that monads aren't really that difficult, but people take time to get them because the concept is so abstract.

10:22 I see

10:23 justin_smith: lazylambda: monads can simplify making and using pure functions that need to carry multiple values (like if I want to chain some function calls and also build up a string in parallel)

10:23 lazylambda: yeah, they are not complex, just hard to intuit

10:24 lazylambda: that's the problem with much of category theory - you don't get it because it's hard to imagine something that insanely general and simple

10:24 lazylambda: many of the inventors and enthusiasts of category theory were fond of calling it "generalized abstract nonsense"

10:25 lazylambda: justin_smith, yes, indeed. I am planning to learn about all of that in the future. I am focusing on clojure right now though. Just got me the 2nd edition of joy of clojure, and the reasoned schemer :D

10:26 justin_smith: lazylambda: clojure is great, and very pragmatic

10:26 bbloom: justin_smith: it's not my idea: https://en.wikipedia.org/wiki/Rule_of_least_power

10:26 justin_smith: lazylambda: we use immutability and function purity because they make great building blocks, but without the mathematical approach of haskell

10:27 bbloom: I should have guessed you did not invent it, thanks for the reference

10:27 lazylambda: justin_smith: exactly what I like about it. Before that, I was using common lisp, which is nice. I think it's bit messy and less aesthetically pleasing though.

10:27 justin_smith: lazylambda: yeah, idiomatic common lisp is often not very functional at all

10:28 lazylambda: justin_smith: indeed

10:30 justin_smith: Clojure did borrow the best ideas of all worlds and combined them in a nice and consistent way.

10:30 justin_smith: bbloom: seeing that article, wow, let me count the ways that modern js / ajax heavy web design violates that principle

10:30 lazylambda: consistent enough at least :) there are rough edges, but the choices are generally very well thought out

10:31 ,(flatten {:a 0 :b 1 :c 2}) ; for example

10:31 clojurebot: ()

10:31 bbloom: justin_smith: honestly, i don't necessarily agree with the way this wiki page frames it

10:31 justin_smith: bbloom: OK

10:32 bbloom: justin_smith: i think the emphasis on turing completeness is misguided -- i'm interested in the power of the typical/encouraged usage. i don't care if it happens to be turing complete, i care that the "pit of success" encourages minimal use of power

10:32 justin_smith: bbloom: as alluded to in the sql example there

10:33 bbloom: justin_smith: ah yes,

10:33 justin_smith: but only vaguely

10:34 lazylambda: justin_smith: that was weird. I thought seq would be called on the arg first before flattening

10:36 bbloom: ~flatten

10:36 clojurebot: flatten is rarely the right answer. Suppose you need to use a list as your "base type", for example. Usually you only want to flatten a single level, and in that case you're better off with concat. Or, better still, use mapcat to produce a sequence that's shaped right to begin with.

10:36 justin_smith: lazylambda: yeah, it is weird, and on top of that even a correctly designed flatten would be a code smell (why isn't your data shaped properly in the first place, are you really willing to make your code so un-general that a collection can't be the base case)

10:37 bbloom: that said, i really do want a auto-splicing vector type :-)

10:37 justin_smith: seems like something you could do with finger-trees right?

10:37 lazylambda: justin_smith: Can you elaborate on that last point?

10:38 bbloom: justin_smith: probably more likely rrb vectors, yeah

10:38 justin_smith: lazylambda: if you need flatten, I take it as a sign that the algorithm that handed you the data to be flattened was poorly constructed because it is handing you inconsistently shaped data

10:39 lazylambda: because with a consistent shape, concat would suffice

10:40 lazylambda: justin_smith: ah yes. so you mean the input may contain different types of collections within the structure you're trying to flatten?

10:41 justin_smith: lazylambda: in practice, if someone thinks they need flatten, we can look at the function generating the data, and figure out where it is intermingling single elements and lists of elements inconsistently, and fix that

10:42 lazylambda: justin_smith: I see

10:42 zenlambda: is there a way to 'escape out of' thread-first (->) ?

Logging service provided by n01se.net