#clojure log - Dec 30 2015

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

0:15 mindbender1: Is boot operations slower than lein operations?

0:19 TEttinger: mindbender1: it's a good question.

0:19 ~faster

0:19 clojurebot: faster is https://github.com/technomancy/leiningen/wiki/Faster

0:19 TEttinger: those are some suggestions for lein, they may be some for boot and some may apply to both (I think drip works for either)

0:22 mindbender1: TEttinger: I just wanted to be sure this was not happening only on my system.

0:23 TEttinger: are you using Boot and it's noticeably slower?

0:30 mindbender1: TEttinger: I have not really taken it for a proper test drive just the time it takes to do `boot -h` and `boot repl` got me concerned.

0:31 TEttinger: mm. lein repl takes a fair bit of time too for me

0:32 mindbender1: I guess I should play with it a bit more. And they better have good documentation for speed optimizations such as the one you pointed to.

0:33 One of the reasons I like lein is the author's will to make lein bendable to the user's will.

0:53 nys-: what does the .nrepl-port file do?

1:01 ridcully: nys-: it contains the port number of the nrepl

1:03 e.g. tools/editors can pick it up. the port usually is random

4:00 favetelinguis: im building a webserver and am now looking for a way to spawn worker processes using core.async, both go blocks and threads will be used, the problem i have is how to manage starting and stopping of the processes at runtime, any known framworks that deals with this?

7:25 nicola: hi - i'm building something with lein uberjar, and i'm deciding where to put the resulting jar file in the unix filesystem (I'm making a debian package). Is there a conventional directory for these?

7:26 there will be a shell script that invokes 'java -cp ... args...' and it will go in /usr/local/bin, but i'm not sure where the jar itself should go

7:46 pvinis: hello. i have a list like (def a '(1 2 3 4)), and a fn that takes 4 args. how can i call that with my list?

7:47 if i do (myfn a) then its just one arg

7:47 can i explode it somehow?

7:50 luma: (apply myfn a)

7:54 pvinis: aha..

7:55 cool. thanks

8:51 favetelinguis: what is the idomatic way to spawn and kill core-async go blocks at runtime? for example i need an go block to what makes rest request every 5sec push this out on a websocket, i need to be able to kill this go block when im done with it but since i want it to be controlled by time i cant use the pattern och blocking on the incomming channel

10:15 justin_smith: favetelinguis seems to have left, but they can block on the incoming channel and a timeout at the same time, by using alts!

10:18 TimMc: clojurebot: mutation is exciting!

10:18 clojurebot: c'est bon!

10:22 g0w: hi everyone.. i'm having some trouble understanding this syntax. How do I pass-in a different value of tempo?

10:22 https://www.irccloud.com/pastebin/3fktOx7x/tempo.clj

10:23 Can't quite the understand the destructuring that's going on in the function argument

10:23 clojurebot: Huh?

10:23 justin_smith: g0w: (test-args [] :tempo 80)

10:23 and we don't really use the & {} thing much any more

10:23 g0w: hello again @justin_smith

10:23 ah okay! thanks a lot!

10:24 what do we generally use in these cases?

10:24 justin_smith: g0w: just a hash map with keys and values

10:24 TimMc: Just drop the & and have a map arg there :-)

10:24 justin_smith: (like what you tried to use)

10:24 yeah

10:24 g0w: got it! thanks!

10:24 TimMc: You can add another arity to default the map to empty or nil.

10:25 g0w: @TimMc yup.. that would be my preference too.. i found this code in a book

10:26 so I had one meta question with immutability in clojure..

10:27 does the immutability thing hold true only for clojure datatypes?

10:27 for example, I was using a BitSet yesterday, and calling (.set bitset) didn't return a new bitset

10:27 justin_smith: the clojure datatypes, strings, and primitives are all effectively immutable

10:27 everything else is mostly mutable, it's the same objects java uses

10:27 engblom: I am new to concurrent programming with Clojure and I want to check up if I understand things right. First, can I from two different threads define the same named promise?

10:28 And if I am able to define the same named promise in both, and I deliver from a third thread, will only one of the other threads quit blocking if both do deref?

10:28 justin_smith: engblom: yes, but it might not do what you want. Where is the name being assigned?

10:28 do you mean def?

10:29 g0w: so the issues with using java objects in writing concurrent code still exist? ie. i would need to manually synchronize access to a bitset object ?

10:29 justin_smith: g0w: pretty much, yeah. And you shouldn't put mutable objects inside atoms or refs either

10:30 g0w: ok.. got it.. what's the accepted best practice in these cases? rely as less as you can on java objects?

10:30 yenda: is there a way to check for more recent dependencies with boot ?

10:30 justin_smith: engblom: to be super pedantic, promises never have names, but using let or def you can bind one to a name, and either will let you bind two things to the same name, and simply render the first one inaccessible by name

10:32 engblom: justin_smith: Assume I have two threads, both doing binding my-promise either through let or def (for example (def my-promis (promise)) ) Then a third thread is started and it does (deliver my-promise "whatever"). Both original threads are doing @my-promise, will one of them continue to block

10:32 ?

10:34 TimMc: g0w: Clojure doesn't change the way Java and the JVM work, it just provides alternatives and wrappers.

10:34 justin_smith: engblom: well, you can't have more than one thread binding values in a single let block, so we have to assume it is defs. and yes, the one waiting on the first promise will wait forever, because nobody else has a handle on the old promise, and nobody will deliver to it.

10:34 engblom: this is just one reason def at runtime is usually wrong

10:35 TimMc: g0w: If you need to modify BitSets, your functions will need to be defensive in the same way that Java methods need to be defensive -- cloning.

10:35 g0w: cloning? can you elaborate a bit more?

10:35 engblom: justin_smith: To be more specific, I am looking at this library: https://github.com/juxt/dirwatch

10:36 TimMc: g0w: If I hand you a mutable object, you might want to create a copy so that I can't accidentally modify your instance.

10:36 g0w: but woulnd't a deep clone of every object be expensive with no structural sharing in place?

10:37 TimMc: It can be, yes.

10:37 justin_smith: but you shouldn't need deep cloning, because you don't need the mutable collection types

10:37 engblom: justin_smith: I wish to use it in this way: Wait blocking until a certain file has changed. So I planned to use promises, and have the function 'watch-dir' will run to deliver

10:37 g0w: justin_smith: BitSet is a mutable collection type, right?

10:38 engblom: But then I begun to wonder if this is thread-safe. What will happen if several processes does the very same on the same file?

10:38 justin_smith: kind of - it's not really a container though, it can only hold bits (a real container can contain any Object type). you don't "deep copy" a bitset, you just copy it

10:39 g0w: @justin_smith: ah yes, I get what you mean.

10:39 justin_smith: engblom: if you set up the promise to be delivered to, the process waiting on the promise, and the watcher to deliver to the promise, all in one local scope, there's no chance of "clobbering". The clobbering would happen if you were using a def and there was a race condition in your def usage.

11:05 jjttjj: I have a connection object from a java library i'm working with. It relies on some external state, for instance a new client-id is needed for each connection. requests can be made from the connection, and each request must have a unique, auto-incrementing ID, and i’m queueing all responses to the requests in a core.async chan. do i just shove all these things in a map when I create a connection and pass it around everywhere?

11:12 justin_smith: jjttjj: there are many ways to handle this. You could use an atom with a map, you could create a go block that lexically closes over each ID and then broadcast via core.async...

11:24 m1dnight_: A bit off topic, but are there some more general programming-related channels I could check out on freenode (or others)?

11:30 sdegutis: m1dnight_: ##programming

11:30 m1dnight_: ##webdev

11:30 m1dnight_: Oh, that is rather obvious :> thanks

11:30 Joined them

11:30 sdegutis: :)

12:04 favetelinguis: i want to conj to a IPersistentCollection, is this possible or how can to clone the IPersistentCollection vector and then use the new to conj what i want?

12:35 TimMc: ,(clojure.string/escape "foo" {\o \e})

12:35 clojurebot: "fee"

12:36 TimMc: favetelinguis: IPersistentCollection is pretty vague. Do you know anything else about the value?

12:51 sfz-: ,(clojure.string/escape "foo.com?q=bar baz qux" {' ' '+'})

12:51 clojurebot: #<RuntimeException java.lang.RuntimeException: Map literal must contain an even number of forms>

12:51 sfz-: hm

12:51 MJB47: use " instead of '

12:51 sfz-: ,(clojure.string/escape "foo.com?q=bar baz qux" {" " "+"})

12:51 clojurebot: "foo.com?q=bar baz qux"

12:52 sfz-: nope, how would you replace a space?

12:52 MJB47: try string/replace

12:53 TimMc: ,(clojure.string/escape "f " {space \o})

12:53 clojurebot: #error {\n :cause "Unable to resolve symbol: space in this context"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.RuntimeException: Unable to resolve symbol: space in this context, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyze "Compiler.java" 6704]}\n {:type java.lang.RuntimeException\n :message "Unable to resolve symbol: space in this...

12:53 TimMc: urgh

12:53 ,(clojure.string/escape "f " {\space \o})

12:53 clojurebot: "foo"

12:53 TimMc: sfz-: ^ But really you wouldn't, you'd use a URL library.

12:53 sfz-: ,(clojure.string/escape "foo.com?q=bar baz qux" {\space \+})

12:53 clojurebot: "foo.com?q=bar+baz+qux"

12:53 justin_smith: favetelinguis: when you call conj on any object implementing IPersistentCollection you get a copy with your data added to the collection

12:54 sfz-: TimMc: right, was just curious, thanks!

12:54 TimMc: I know ring lib has stuff for that, still a relative n00b to clj though so trying to pick up all the tricks I can

12:55 justin_smith: ,(java.net.url/URLEncode "foo.com?q=bar baz qux" "UTF-8")

12:55 clojurebot: #error {\n :cause "java.net.url"\n :via\n [{:type java.lang.ClassNotFoundException\n :message "java.net.url"\n :at [java.net.URLClassLoader$1 run "URLClassLoader.java" 366]}]\n :trace\n [[java.net.URLClassLoader$1 run "URLClassLoader.java" 366]\n [java.net.URLClassLoader$1 run "URLClassLoader.java" 355]\n [java.security.AccessController doPrivileged "AccessController.java" -2]\n [java.net.U...

12:56 justin_smith: ,(java.net.URLEncoder/encode "foo.com?q=bar baz qux" "UTF-8")

12:56 clojurebot: "foo.com%3Fq%3Dbar+baz+qux"

12:56 justin_smith: really you would only encode the part that goes after the ? of course

12:56 sfz-: right

12:56 thanks again

12:56 TimMc: ,(->> (range 256) (map char) (filter #(not= (count (pr-str %)) 2)))

12:56 justin_smith: ,(str "foo.com?" (java.net.URLEncoder/encode "q=bar baz qux" "UTF-8")

12:56 clojurebot: #<RuntimeException java.lang.RuntimeException: EOF while reading>

12:56 (\backspace \tab \newline \formfeed \return ...)

12:57 justin_smith: ,(str "foo.com?" (java.net.URLEncoder/encode "q=bar baz qux" "UTF-8"))

12:57 clojurebot: "foo.com?q%3Dbar+baz+qux"

12:57 TimMc: except URLEncoder is wrong :-(

12:57 justin_smith: TimMc: I still think I'm using it wrong...

12:57 TimMc: and this is where replace actually does come in

12:57 sfz-: ,(ring.util.codec/url-encode "clojure url")

12:57 clojurebot: #error {\n :cause "ring.util.codec"\n :via\n [{:type java.lang.ClassNotFoundException\n :message "ring.util.codec"\n :at [java.net.URLClassLoader$1 run "URLClassLoader.java" 366]}]\n :trace\n [[java.net.URLClassLoader$1 run "URLClassLoader.java" 366]\n [java.net.URLClassLoader$1 run "URLClassLoader.java" 355]\n [java.security.AccessController doPrivileged "AccessController.java" -2]\n [java...

12:57 sfz-: aw, no clojars eh?

12:57 justin_smith: sfz-: clojurebot won't have any ring stuff

12:57 TimMc: justin_smith: https://github.com/timmc/johnny/blob/master/src/main/java/org/timmc/johnny/Codecs.java#L28

12:58 justin_smith: sfz-: it might use clojars, but that's not how you add something ot the classpath

12:58 TimMc: ahh

12:58 TimMc: wait, that's decoding... but you just do it in reverse

13:47 jjttjj: how should i clojureize the name of the java class EClientSocket... "e-client-socket"? "eclientsocket"? first seems more typical but looks weird here is the second one dumb to do?

13:48 maybe i'll just do ecs/ECS?

13:49 justin_smith: why are you clojureizing a class name?

13:49 jjttjj: i need to stick the connection object in a map, trying to figure out what to name the key

13:51 justin_smith: I would usually use a key like :socket - depending on context etc. of course

13:51 jjttjj: justin_smith: cool thanks! getting hung up on dumb things today

14:02 TimMc: :☃-socket

14:03 justin_smith: TimMc: well, it is a Snowman, so as an initialism :☃ ocket should work

14:07 TimMc: nice, even more concise

14:07 justin_smith: it even has a compatible outline

15:08 TimMc: Incanter doesn't appear to have fourier transforms... what am I missing?

15:36 justin_smith: TimMc: clearly they expect every user to hand-roll their own fft

15:36 creese: I've been reading about om.next. I'm wondering what if anything has been done on the back-end portion. Has anyone build a ring app that takes om.next queries and return responses using a datastore other than Datomic?

15:37 Kajtek: Hi guys, so I've run into a problem when using map destructuring and lazy sequences.

15:37 Basically, whenever I pass a lazy seq with odd number of elements to a function that uses destructuring I get an exception.

15:37 Even-numbered sequences work as expected.

15:38 Here's a minimal reproduction: ((fn [{:keys [k]}] k) (lazy-seq [{:k :a}]))

15:38 Is it a known bug? I don't really follow Clojure development.

15:38 Also, this happens on Clojure 1.7.0.

15:38 MJB47: {:keys...} only works correctly if the input is a map

15:38 justin_smith: Kajtek: that argument is incorrect

15:38 MJB47: you have a sequence of maps

15:39 justin_smith: if you replaced [{:keys [k]}] with [[{:keys [k]}]] in the function definition it would work

15:39 (but other calls would likely break)

15:40 Kajtek: Then it working for even-numbered seqs is kind of misleading.

15:40 justin_smith: Kajtek: yes, it works accidentally, and is unlikely to give a correct result

15:40 Kajtek: there are many things in clojure that work accidentally

15:41 ,(:or :a :b) ; a classic mistake, which might even pass your tests sometimes

15:41 clojurebot: :b

15:42 Kajtek: I guess warts happen.

15:42 Thanks for the help. I suppose I'll get rid of destructuring and check if the argument passed is a map beforehand.

15:43 ystael: justin_smith: *chokes on popcorn* that's a trap that never occurred to me

15:43 justin_smith: ystael: I've seen it!

15:43 ,(:or false :a) ; the version that looks like it works

15:44 clojurebot: :a

15:44 ystael: i don't think i knew that keyword-as-map-extractor could take a second arg

15:44 justin_smith: ystael: see also (:require '[it doesn't matter what you put here]) - returns nil, as you expect

15:44 and of course, does nothing at all

15:45 ystael: this sounds like an argument for tutti frutti syntax coloring

15:46 justin_smith: heh - or a stricter programming language :P

15:46 ystael: Well, yes, but this is #clojure :)

15:46 justin_smith: ystael: syntax highlighting wouldn't work with the :require mistake - you expect it to be a keyword, you just don't realize it only works like that inside the ns macro...

15:47 ystael: Fortunately I typically err in the opposite direction instead, typing (require '[...) inside the ns macro

15:48 justin_smith: it's an easy typo/thinko to make, because the syntaxes are so similar, and they do the same thing, they just happen to be incompatible and one of them tends to be a silent noop

15:48 ystael: ah well, I use clojure because it's a good set of practical compromises, not because I think it's perfect :)

15:48 justin_smith: thinko is of course short for thinkographical error

15:50 * TimMc updates wikipedia

16:00 jiamo: I find hard to transalte this algorthm into clojure

16:00 n=”the evil big number” if n mod 2=0 then lastFactor=2 n=n div 2 while n mod 2=0 n=n div 2 else lastFactor=1 factor=3 maxFactor= n while n>1 and factor<=maxFactor if n mod factor=0 then n=n div factor lastFactor=factor while n mod factor=0 n=n div factor maxFactor= n factor=factor+2 if n=1 then output lastFactor else output n

16:01 It is a Procedural paradigm

16:02 justin_smith: jiamo: the trivial transormation is to turn the while into a loop, and make every value mutated inside the while into a loop binding

16:02 that doesn't always give the best result of course, but its the version that is easiest to translate

16:03 nested while might require a let binding to bind the loop variables coming out from the inner loop

16:04 also, if you have multiple mutations inside one cycle, you can use shadowing in a let block, or explicitly transform it into multiple vars, each chaining from the previous one's value

16:13 oh, how sad, I started to play with how one would translate that, then realized the block syntax in that pseudo code is totally ambiguous and jiamo isn't here to ask for clarification

16:16 sohail: is there a way to dispatch on the values in a vector without unpacking it just for dispatch? Specifically, I have a vector that looks like this: [:keyword {..}]

16:17 justin_smith: sohail: what do you mean by "without unpacking it" ?

16:18 sohail: justin_smith: I mean without doing something like (my-multi (first data) (second data))

16:18 justin_smith: (defmulti my-multi first)

16:18 sohail: oh!

16:19 I think I need to read this stuff more carefully

16:19 justin_smith: or maybe (defmulti my-multi (comp type first)) if you still want to dispatch by type...

16:19 and that needs adjusting if your method takes multiple args too, of course

16:20 sohail: there are a lot of examples of defmulti on grimoir - it can do a lot!

16:20 sohail: what is grimoir?

16:20 justin_smith: http://conj.io/store/v1/org.clojure/clojure/1.7.0/clj/clojure.core/defmulti/

16:20 conj.io is grimoir

16:21 incidentally, I fixed and/or wrote most of the defmulti examples myself

16:22 sohail: awesome, thanks justin_smith

16:24 amalloy: justin_smith has accidentally betrayed his alter-ego, MultiMethodMan

16:25 justin_smith: what's that? commisioner Gordon has lit the dispatch signal - wait a moment while I look up the method that signal dispatches to

16:28 amalloy: oh no! the joker has replaced your dispatch function with (#(% %) #(% %))! now you'll never get to the crime scene in time!

16:28 justin_smith: haha

16:29 holy combinators batman!

16:34 TimMc: I want to bucket a list of monotonically increasing numbers by multiples of 1000, e.g. [1 35 456 2005 3045 3987] => [[1 35 456] [] [2005] [3045 3987]]

16:34 I think my best bet is a loop and split-with, but is there something even simpler?

16:35 justin_smith: ,(group-by #(count (str (/ % 1000))) [1 35 456 2005 3045 3987]

16:35 oops

16:35 clojurebot: #<RuntimeException java.lang.RuntimeException: EOF while reading>

16:35 justin_smith: ,(group-by #(count (str (/ % 1000))) [1 35 456 2005 3045 3987])

16:35 clojurebot: {6 [1 456], 5 [35], 7 [2005 3045], 9 [3987]}

16:36 justin_smith: oh, that wasn't a truncating divide, d'oh

16:37 TimMc: The key is that I also want empty buckets.

16:37 justin_smith: ,(group-by #(int (/ % 1000)) [1 35 456 2005 3045 3987])

16:37 clojurebot: {0 [1 35 456], 2 [2005], 3 [3045 3987]}

16:37 justin_smith: ahh

16:38 TimMc: maybe a trick with (comp max keys) and range and (fnil (m %) [])

16:38 TimMc: hrm

16:38 ystael: or you could just write the reduce by hand

16:38 luma: ,(mod 2005 1000)

16:39 clojurebot: 5

16:39 luma: ah, not modulo

16:39 ystael: (you have reduce, why do you need anything else? :))

16:39 TimMc: I think loop is better here since I need to bind 2-3 values on each iteration.

16:39 luma: ,(quot 2005 1000)

16:39 clojurebot: 2

16:39 luma: this one

16:40 justin_smith: luma: yeah, better to use quot than (comp int /)

16:42 TimMc: Here's my solution: https://gist.github.com/timmc/35c969e97ac1c5e7f72f

16:43 justin_smith: (let [in [1 35 456 2005 3045 3987] by-count (group-by #(quot % 1000) in)] (map #(get by-count % []) (range (inc (apply max (keys by-count))))))

16:43 ,(let [in [1 35 456 2005 3045 3987] by-count (group-by #(quot % 1000) in)] (map #(get by-count % []) (range (inc (apply max (keys by-count))))))

16:43 clojurebot: ([1 35 456] [] [2005] [3045 3987])

16:44 TimMc: "now make it lazy"

16:44 justin_smith: :)

16:45 TimMc: I've made a maximally lazy coll-of-colls splitter like this before and it was annoying to write: https://github.com/timmc/rand/blob/master/src/rand/core.clj#L26

16:46 The approach was to lazily inject sentinels at the boundaries, then lazily group on the sentinels.

16:58 justin_smith: ,(let [ord #(quot % 1000)] (apply mapcat (fn [prev sub] (concat (repeat (dec (- (ord (first sub)) (ord (first prev)))) []) [sub])) ((juxt identity rest) (cons [0] (partition-by ord [1 35 456 2005 3045 3987]) )))) ; bizarre, but works and is lazy

16:58 clojurebot: ((1 35 456) [] (2005) (3045 3987))

16:59 justin_smith: there is probably a way to make that less loony

17:02 also that [0] should probably be a -1 and the behavior around various orders of magnitude as starting values probably needs fixing

17:05 tolstoy: clojars still out, eh?

17:06 justin_smith: oh wow, what happened?

17:06 tolstoy: No idea. My "lein ancient" was having issues. Checked: https://linode.statuspage.io, which clojars mentioned on their twitter account 3 days ago.

17:07 Ah, it's back in the browser, now.

17:07 justin_smith: tolstoy: https://twitter.com/clojars

17:07 oh, d'oh, while I am looking that up you mention their twitter, heh

17:07 tolstoy: Yeah, that was Dec 27.

17:08 justin_smith: do you know which location they are using?

17:08 tolstoy: Okay, it's back.

17:08 No idea.

17:11 TimMc: linode is being DOS'd

17:11 Has been for a couple days, I think?

17:12 tolstoy: Fastmail's been having a problem as well.

17:12 AIs getting a little feisty?

17:19 TimMc: Someone's probably trying to extort them.

17:53 justin_smith: repeating my core.async / cljs question here because #clojurescript is looking a bit underattended right now

17:53 OK, I have an issue that comes up because <!! is missing in cljs

17:53 in my cljs startup, I need to delay the rest of the app coming up until I get a specific message on a core.async channel

17:54 in other words I need to block until I get a message - but can't use <!!

17:54 is there a sane / normal way to do this? - I understand realistically that the code which sends the message would be blocked if I used <!!, but I also don't want to suck all my init code up into a go block just so I can synchronize on a channel at one place

17:54 tolstoy: Are you using one of the reacty things?

17:54 justin_smith: yes, but none of the reacty crap is running yet when this happens

17:55 tolstoy: Oh. I was going to suggest just run as normal, but set a prop in the state such that nothing gets rendered.

17:55 justin_smith: this is during initial page load, handshaking a websocket with the server, and then I need a specific message from the server before the rest of the code in the app can safely run

17:55 tolstoy: When the appropriate message comes in, reset that, and the GUI will pop-up.

17:55 justin_smith: tolstoy: yeah, this is about more than props

17:55 right

17:56 tolstoy: (swap! state assoc :view :loading)

17:56 I do that, anyway.

17:56 justin_smith: tolstoy: I'm actually using stuart sierra's component lib, and this is a component init, and really I don't want to return from the init of this component until the confirmation message (and the data it sends) is present

17:56 tolstoy: If (:user state) is nil, render a loading component. Eventually, data comes in and it works.

17:57 justin_smith: other components do things (before react renders) that will break badly if this data is not present

17:57 tolstoy: Oy.

17:57 justin_smith: and I can't just put the data in the cljs file

17:58 tolstoy: That's a bit of a monkey-wrench.

17:58 bja: justin_smith: could you use take! and write a function that delivers to a promise then deref that?

17:58 justin_smith: bja: no promises in cljs!

17:58 I thought of that one too

17:58 bja: could you use $.deferred or something likethat?

17:58 justin_smith: hmm... we aren't using $ at all, but maybe there's an alternative I could use

17:59 bja: isn't there a pure-cljs promise library

17:59 justin_smith: bja: maybe, I didn't check for that

17:59 bja: https://github.com/funcool/promesa

17:59 justin_smith: it would be just the thing I guess as long as it played nicely with async stuff that needs to happen

17:59 tolstoy: https://github.com/jamesmacaulay/cljs-promises

18:00 justin_smith: and when I say that, I start to think maybe I do need to suck my whole component system into core.async... oh no

18:00 tolstoy: Maybe have a whole bootstrap system, then invoke component stuff once it's complete?

18:01 justin_smith: maybe I could pull this component out of stuartsierria/component and start it first

18:02 usint take with a "start all the other components" as the callback?

18:02 clearly we need "components, but async"

18:02 bja: does component work in cljs now?

18:03 tolstoy: (go (let [data (<! (bootstrap))] (start-system data)))

18:03 justin_smith: yeah - I did my own cljc port ages ago, and eventually I will switch to the official cljc port which came out more recently

18:03 tolstoy: exactly

18:03 tolstoy: that's probably what I need to do, thanks

18:04 the sad thing is that I had been using a shared component for the websockets - same code for frontend and backend, this would mean splitting them

18:05 but it's probably for the best, there was a high density of conditionals already

18:06 tolstoy: I've never have luck with shared code until the end of a project. That's when I figure out the abstractions I actually needed.

18:07 bja: as an aside, people who start nrepls outside of lein repl and use components, do you have an nrepl component? Do you start it outside the rest of your system?

18:09 tolstoy: Maybe take a config prop and add it to the SystemMap depending on that prop?

18:10 bja: maybe

18:11 justin_smith: yeah - I would make a component that is either optionally part of the system map, or optionally starts the nrepl

18:11 tolstoy: Yeah. Could just take a config and then when "start" is called, does (potentially) nothing.

18:11 I like that better.

18:11 justin_smith: if you have lots of things that conditionally start, then it can be cool to manipulate that in the system map rather than having lots of conditional short circuits in the components

18:12 bja: right now mine just conditionally starts

18:12 justin_smith: I even made a simple "resolver", which says "start my system, but without foo" and it removes foo, and anything that depends on foo, before starting the system

18:12 bja: or rather, it always tries to start, but we accept failure as an option, and we never actually stop it

18:13 justin_smith: it's all data!

18:13 bja: from when I was doing storm stuff I was making a lot of systems and so I represent logical systems as maps and then merge those together before creating the system map

18:14 since different bolts needed different stuff

18:14 so I ended up with a myriad of systems

18:14 I also have a settings component

18:15 that, out of the box, uses environ to resolve settings, reconciles those against a schema for the system, and if all that works, continues

18:15 justin_smith: cool

18:15 bja: it's pretty cool what you can do with something represented as data structures

18:15 bja: so that my settings are available in the map (to be depended on (and destructured, but that's a different story))

18:16 so that components can want something like db-uri, but I can destructure/rename analytics-db-uri for that component

18:17 yeah

18:17 the basis of most of the "amazing wins" (to some of the people I work with) are just due to representing as much of my world as data as possible

18:18 leads to some really cool wins like being able to use the same query language to define data permissions as well as store partial filters and execute real-time matches against incoming docs

18:20 justin_smith: this line of thought makes me think we could do some really amazing things if we made better data validation tools (prismatic/schema is great, but I think other tools could be useful too (even if I don't know what they look like yet))

18:21 tolstoy: Yeah, even just storing the config as EDN (or JSON, I guess) and using prismatic really makes QA think you're with it.

18:21 bja: yeah

18:22 basically all of our apps have something like this: https://gist.github.com/emidln/005fa93f70744cc5fe5f

18:23 it's very nice to be able to be able to know exactly where to look for config settings and what their expected format is

18:23 also nice not having to validate that all over the app

18:23 justin_smith: very cool

18:26 tolstoy: Speaking of p/schema: vectors of variable types. Not [(s/enum t1 t2 t3)]?

18:30 justin_smith: (s/validate [s/Any] [:a 1]) works

18:30 depends what all needs to be in there?

18:30 Any may be broader than what you want of course

18:31 tolstoy: {:id uuid :type :a :data [...]} with three different "type" values. The data depends on the type.

18:31 I can make a schema for each one, but not sure how to put them in the envelope.

18:31 justin_smith: oh, so what is allowed in :data needs to be calculated based on the value for :type

18:32 tolstoy: Yeah. Kinda like {:id "asda" :type :num :data 23}.

18:32 justin_smith: once you have that piece of data, you can do (s/validate [(:type m)] (:data m))

18:32 wait, that's not in a vector!

18:33 you said it would be in a vector

18:33 tolstoy: Well, I'm trying to be abstract.

18:33 Imagine a collection schema with three possible shapes. Enum isn't working.

18:33 Perhaps "maybe".

18:33 justin_smith: tolstoy: sure - general idea, you can look up the schema (in a map you make for example) based on the :type tag

18:33 then validate the :data key

18:34 tolstoy: Ah, so a two part validation step.

18:34 justin_smith: right

18:34 this is a lisp after all!

18:34 tolstoy: this is of course more fiddly and less general than a regular schema

18:35 there may be a way to "hoist" this concept into a proper schema that validates if the :data and :type keys agree in the way we are describing

18:35 tolstoy: Yeah. Seems like they'd have a way to validate a heterogeneous collection of values.

18:35 Given that's allowable in Clojure. Perhaps it's just too ill-advised. ;)

18:36 Maybe "either" is it.

18:37 Deprecated. Hm. "conditional" I guess.

18:38 rhg135: pred is also an option

18:40 tolstoy: java.lang.IllegalArgumentException: No implementation of method: :spec of protocol: #'schema.core/Schema found for class: clojure.lang.Keyword

18:41 *sigh*

18:42 Can you validate exact keywords?

18:42 justin_smith: tolstoy: this is why I was suggesting looking up the schema in a hash map, based on the key under :data

18:42 oh, I misunderstood

18:43 tolstoy: I think there's a facility for that (s/contitional :a ThisSchema :b ThatSchema).

18:43 Assuming :a is invoked as a function in the value. No actual example of contitional.

18:44 rhg135: s/eq

18:45 that validates equality

18:46 tolstoy: thanks!

18:46 Hm. The s/conditional is "cond" like, but how do you get a reference to the value?

18:48 rhg135: I think the first args of the pairs, like :a or :b in your example, are ifn

18:49 predicates

18:49 tolstoy: I end up having to do (s/condition (= #(:type %) :text) TextSchema ..).

18:49 Now I get a "value does not match schema" and it prints 15 lines. ;)

18:55 Hah! Worked!

18:56 rhg135: yay

18:56 tolstoy: (s/conditional #(= (:type %) :foo) FooSchema #(= (:type %) :bar) BarSchema)

18:56 justin_smith: tolstoy: sweet

18:56 TIL

18:56 tolstoy: Sure is nice, in Cider, to just C-x, C-e stuff and have to shoot out the side of the function.

18:58 justin_smith: It was basically what you said, except inside the DSL.

18:58 rhg135: now just factor it out to a type? function that returns a predicate, and BAM!; declarative!

18:58 tolstoy: Yeah.

18:58 justin_smith: (inc rhg135)

18:59 rhg135: I'm glad they finally implemented this

19:02 tolstoy: It's almost like having algebraic types.

19:03 rhg135: if you squint hard, defschema looks like type

19:08 tolstoy: I kinda like the schema emphasis on data validation rather than type signatures. Feels like lateral thinking compared to most language concerns.

19:51 TimMc: justin_smith: Something went *deeply* wrong with that bucketing code I wrote. I know that it should only have about 4 items per bucket, but I graphed the results, and 2 of the buckets ended up with ~30000 items.

19:51 justin_smith: woah

19:51 TimMc: ohshi-

19:52 justin_smith: TimMc: well I did show you that lazy version (which might have an issue or two still, but not like that...)

19:52 TimMc: I know what went wrong! The data wasn't monotonically increasing, that's how I screwed up.

19:52 hahahahaha

19:52 justin_smith: oh, that would do it!

19:53 TimMc: I wonder how long it takes to sort a million timestamps.

19:53 justin_smith: one million

19:55 TimMc: O(100000*log(1000000))

19:56 justin_smith: I had tried to do a fourier transform on the event counts per second and got this: https://i.imgur.com/XODAZSp.png

19:57 justin_smith: pretty!

19:57 TimMc: yeah!

19:57 (the graph oscillates between two curves, creating a solid appearance)

19:58 But clearly screwed up, since I was expecting something mostly flat with a big spike and a couple side spikes.

19:58 justin_smith: at this moment I would like to say that stuartsierra's decision to make hash maps implement component by returning themselves when you call start was a very smart decision that makes this refactor very easy

19:59 tolstoy: Heh. That really screwed me up for a while, missing that fact.

20:00 How come component worked in the repl, but not when starting via a main method?

20:00 (I thought the SystemMap contained an atom or something.)

20:33 athan: Does clojure like monads?

21:25 tolstoy: athan: https://github.com/clojure/algo.monads ?

21:37 princeso: 1) write 0. (zero dot) in your REPL. 2) put the cursor between them (0|.) . 3) write a digit 4) ?? 5) profit.

21:38 justin_smith: princeso: what was that supposed to do?

21:38 g0w: https://usercontent.irccloud-cdn.com/file/LiQkSixv/Screen%20Shot%202015-12-30%20at%209.37.40%20PM.png

21:39 princeso: this is expected, right?

21:39 princeso: justin_smith There is some bug that prevents from writing digits. i dont know.

21:39 justin_smith: princeso: not in my repl

21:40 g0w: princeso: works for me too

21:40 justin_smith: princeso: what repl - maybe it's a bug in your editor or terminal?

21:42 g0w: anyone done with problem 8 with advent of code? I'm stuck on it since like forever :|

21:43 princeso: it is the one provided by CCW i guess.

21:49 justin_smith im using this clojuresque:clojuresque-nrepl:1.0.0

21:50 justin_smith: princeso: CCW has been unsupported for a while, right?

21:52 princeso: g0w that was like ... you profited

21:53 g0w: :D

21:55 princeso: what repl are you using?

21:58 g0w what problem is that?

21:59 g0w: me? the standard one... lein repl - http://i.imgur.com/PVbYrDX.png

21:59 justin_smith: nice prompt string

22:00 princeso: good

22:00 tester: hey all

22:00 g0w: princeso: the problem about escaping strings - having a hard time figuring out how to escape the ", \ etc. http://adventofcode.com/day/8

22:00 thanks @justin_smith

22:01 justin_smith: g0w: oh, interesting - did you check out pr-str ?

22:02 ,(pr-str "\"")

22:02 clojurebot: "\"\\\"\""

22:02 justin_smith: ,(count (pr-str "\""))

22:02 clojurebot: 4

22:02 g0w: ,(count (pr-str "\x27"))

22:02 clojurebot: #<RuntimeException java.lang.RuntimeException: Unsupported escape character: \x>

22:02 g0w: how do i get the \x27 testcase to work?

22:02 justin_smith: yeah, we don't use the same escapes

22:02 you need an adaptor I guess...

22:03 ,(count (pr-str "\\x27"))

22:03 clojurebot: 7

22:03 justin_smith: they they don't expect "\\x27" to print as \x27

22:04 g0w: "\x27" is 6 characters of code

22:04 that's the part i'm struggling with

22:06 justin_smith: well, it's 6 characters in some language

22:06 illegal in ours

22:06 g0w: yup

22:07 but if we can't somehow make it match 6, we wouldn't be able to make the testcase pass right?

22:08 justin_smith: g0w: oh, you could compare count of str of the string to count of print-str of string

22:08 ,(map count (juxt str print-str) "\\")

22:08 clojurebot: #<IllegalArgumentException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.core$juxt$fn__4498>

22:09 justin_smith: ,(map count ((juxt str print-str) "\\"))

22:09 clojurebot: (1 1)

22:09 justin_smith: err

22:09 I know there is a way to make this work!

22:10 ,(map count ((juxt str pr-str) "\"\\x27\""))

22:10 clojurebot: (6 11)

22:13 g0w: the output we're looking for is (6 1) right/

22:13 ?

22:14 justin_smith: yeah, so that's not helping at all

22:14 you would need your own rule to turn \xNN into a single result

22:15 ,\u0027

22:15 clojurebot: \'

22:15 justin_smith: turn every \x into a \u00 ?

22:15 amalloy: it's really not hard to implement that by hand. trying to use pr-str and such is cute, but not really worth the hassle

22:16 justin_smith: yeah, probably not, you're right

22:16 amalloy: like my solution in haskell was trivially short, and very simple/clear: https://github.com/amalloy/advent-of-code/blob/master/day8/src.hs

22:17 g0w: ,(- (count "\"\u0027\"") 2)

22:17 clojurebot: 1

22:18 g0w: @amalloy: is this for part 1 or part 2?

22:18 amalloy: both. the first function is part1, the second part2

22:19 g0w: is that pattern matching?

22:19 amalloy: yeah

22:19 g0w: sorry.. i've never read haskell code before

22:20 amalloy: you could do something similar in clojure with a bunch of cond clauses

22:20 justin_smith: or the silly way, with multimethods

22:21 amalloy: (cond (empty? s) 2, (.startsWith s "\\x") (+ 3 (overhead (drop 4 xs))) ...)

22:21 though i guess you'd need to use subs instead of drop, if you want to use string operations

22:22 multimethod man to the rescue?

22:22 g0w: amalloy: the trick is to replace "\x" into a \u00?

22:22 amalloy: well that was justin_smith's proposal

22:23 justin_smith: I think amalloy has the better plan here

22:23 amalloy: i'm just saying to write a recursive function on a string, and if you find it starts with \x then remove 4 characters from the string and add 3 to the recursive call

22:23 g0w: amalloy: got it.. let me give this a shot

22:26 amalloy: justin_smith: i did try implementing it with haskell's versions of pr-str and read-string though, first

22:26 princeso: ,(type .123456789012345678901234567890)

22:26 amalloy: i think it's a reasonable shortcut to try to take, but if anything goes wrong with that shortcut i'd just abandon it and do it by hand

22:26 clojurebot: #error {\n :cause "Unable to resolve symbol: .123456789012345678901234567890 in this context"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.RuntimeException: Unable to resolve symbol: .123456789012345678901234567890 in this context, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyze "Compiler.java" 6704]}\n {:type java.lang.RuntimeException\n...

22:27 justin_smith: princeso: numbers can't start with a . in clojure, you need 0.

22:28 princeso: justin_smith that makes sense

22:29 justin_smith: I think it's partially because we use . so much for interop stuff? dunno

Logging service provided by n01se.net