#clojure log - Apr 09 2015

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

0:00 Igor: But when I reset! atom, if I first need calculate new value, depends on this atom, another thread can running between calculating and reset!

0:00 justin_smith: yes

0:00 Igor: Hmm.. I think I must use swap! in this case =)

0:01 justin_smith: reset! is for if you unconditionally want to set the value, swap! is if you want to calculate a new value based on an old one, and you want to recalculate if there was any modification in the meantime

0:07 Igor: thx

1:28 grehuo: Hi everyone. I've got a newbie problem, that I'm stuck at and can't figure out from the docs. I'm creating a todo app, and have my state in an atom like this: http://pastebin.com/ETgz4b5g. I want to make a function to change the state of one of the items in the vector, so that it has {:done true}. I think the function I'm looking for is assoc-in, but I can't get it to work like I want to and can't figure out how to use it. Does anyone

1:28 know how you would update one specific map in the vector in the atom?

1:30 Basically: how can you swap! a value of a map inside a vector inside an atom, without making changes to the other parts of the atom?

1:31 nuwanda__: grehuo:

1:32 you want assoc-in

1:32 with [index :done]

1:33 ,(def todos [{:id 1 :done false} {:id 2 :done true}])

1:33 clojurebot: #'sandbox/todos

1:33 nuwanda__: ,(assoc-in todos [0 :done] true)

1:33 clojurebot: [{:id 1, :done true} {:id 2, :done true}]

1:37 grehuo: I wrote `(swap! todos (assoc-in @todos [0 :done] true))`, but am getting the error "No item [the vector] in vector of length 2". This is cljs. Also, this will only update the first item, every time right? Not any item I chose?

1:45 tomjack: (swap! todos assoc-in [0 :done] true)

1:45 true

1:46 it seems maybe a bit odd to have a vector of maps with ids -- do you want to update by id, or by position in the vector?

1:46 0 in [0 :done] means "the first element"

1:47 if you want to update by id, it might be easier to have (def todos (atom {1 {:done false} 2 {:done true}})), then (swap! todos assoc-in [1 :done] true) means "the element under key/id 1"

1:50 (swap! todos assoc-in [0 :done] true) probably looks mysterious, it's shorthand for (swap! todos #(assoc-in % [0 :done] true))

1:50 when swapping you must take the current value as a function parameter, _not_ dereference like @todos

1:51 (reset! todos (assoc-in @todos [0 :done] true)) would be the non-exception-throwing way to do what you tried, but it would be wrong

1:51 grehuo: I see.. Yeah having :id in a vector makes no sense, removing it and using position in vector as id instead.

1:51 tomjack: because you may dereference todos, then someone else swaps the atom, then you reset, losing state

1:53 (OTOH having :id in a vector could make sense, if e.g. the todo items can be moved around in the list but you want to remember their identities :) )

1:55 s/losing state/possibly causing any number of weird race condition problems/

1:56 grehuo: nuwanda__ tomjack I'm starting to make it work now, this is so cool! Now I just have to figure out how to select the index of the todo that is being clicked on in my app, but I'll have a go at that myself :). I can not thank you enough for helping me figure out what was wrong in my example, and helping me understand assoc-in! THANK YOU!!!

1:57 * grehuo sending roses and chocolates

1:57 grehuo: The solution seems to be to not set the :id

1:57 and then do (swap! todos assoc-in [0 :done] true), like you saik

1:57 said

2:17 celwell: Hello, I find I'm having to use this quite often (when (not *compile-files*) ...) and it's dirtying my code up. Any ideas? Is there a prettier solution? The reason I need that conditional is because those expressions cause uberjar (and uberwar) to hang if they are run at compile time.

2:19 mavbozo: celwell, interesting, are those expressions have side-effects?

2:21 celwell: mavbozo: e.g., one starts a thread that modifies a db every 5 seconds

2:22 sveri: celwell: I have never seen something like this before. I would say you have some code which you are evaluating during compilation / macro evaluation maybe which causes your process to hang

2:23 mavbozo: celwell, those expressions are (defns ...) ?

2:23 celwell: heres an example:

2:23 (when (not *compile-files*)

2:23 (def zones (map #(assoc % :zip_codes (util/split-on-comma (:zip_codes %)))

2:23 (get-all-zones (db/conn)))))

2:24 where, get-all-zones grabs from db

2:24 if I take off the (when (not *compile-files*) it will hang on ring uber war

2:24 or whatever the command is

2:24 sveri: because of the (get-all-zones... I assume

2:25 celwell: yeah

2:25 mavbozo: celwell, you should't put side-effecty expressions at the top-level of your namespace.

2:25 sveri: celwell: read about component: https://github.com/stuartsierra/component this is valuable for what you want to do

2:26 mavbozo: celwell, put it inside a function and call those function in your (defn -main [] ...)

2:26 celwell: I don't have a -main currently.

2:26 i guess i should read about it?

2:27 I'm lein ring server, and then making uberwars for deployment

2:28 sveri: Does an uberwar work at all without a -main method?

2:28 celwell: yeah

2:29 This is the entrance to the program I guess:

2:29 (def app

2:29 (-> (handler/site app-routes)

2:29 (middleware/wrap-json-body)

2:29 (middleware/wrap-json-response)))

2:29 sveri: hm, interesting. still, I find the idea very unusal not to have one entry point

2:29 sobel: -main is not the entry point for a war

2:29 celwell: I guess ring handles the -main business, unless I'm mistaken

2:29 sobel: well, not your -main

2:29 :)

2:32 sveri: sobel: not? How does the container figure out which class and method to start?

2:36 sobel: sveri: afaik, that's part of the servlet spec

2:39 sveri: sobel: right, I remember, you can define an entry point in some xml file. Been to long since I had to use it

2:45 sobel: sveri: what entry point do you mean? there is a -main that brings up the server, but it's ring's

2:45 all the entry points you define for a web app are request handlers

2:52 sveri: sobel: Yea, It's been some time, but once I worked with GWT and I had to define a class as an entry point in some xml file somewhere

3:25 dysfun: hrm, have to say i'm rather liking boot so far

3:25 even if i'm missing about a dozen plugins i quite like

3:34 mavbozo: celwell, if you use lein-ring plugin you can specify your init function in :ring options in project.clj

3:35 celwell, the init function called once before your handler starts

4:00 mpenet: just wondering: any advantage of using an uberwar vs uberjar?

4:01 dysfun: mpenet: there is if you deploy via an appserver

4:01 mpenet: yes, appart from that

4:03 I guess an appserver is mandatory in prod if you use war files

4:06 dysfun: i think they can still be executable

4:06 it's just that war files mandate all that xml as well

4:07 * dysfun deploys to production by running lein uberjar && rsync -av

4:07 dysfun: (well, for stuff that doesn't matter)

4:08 * mpenet uses uberjars too

4:08 dysfun: there's no need to have an appserver for a good production environment either

4:08 i certainly don't have the level of experience required to actually administer an appserver

4:09 mpenet: I guess if I had to use an app server (as per client request), wars make sense, otherwise not so much

4:09 dysfun: that's a good rule of thumb

4:09 sveri: me uses an uberjar too

4:09 dysfun: there are some circumstances where appservers win, like if you're using J2EE stuff, but in clojure, there's really no need to use J2EE stuff

4:10 it's mostly handy to have it there as a way of integrating with an aging java codebase

4:11 sveri: I guess appservers also make sense if you want multiple apps running inside one server to save resources

4:12 dysfun: there's some of that, but it starts to get complicated very quickly

4:12 memory is cheap. i hope your time is not :)

4:13 back in the day, appservers were the only way to make performant web stuff

4:13 then we got Netty. Then it got good. really good.

4:13 now i deploy netty in an uberjar (via aleph)

4:15 sveri: dysfun: Well, there are people running vps with only limited memory, so for some this is a concern. That said, I am on your side too, I'd rather pay a bit more before I go ahead and fiddle with theses app servers and all the complexity they bring in

4:15 dysfun: there's also something to be said about valuing your sanity :)

7:04 blashyrk: hello, is it possible to convert a hashmap into an atom?

7:04 or at least construct an atomic hashmap from a normal, immutable map?

7:05 TEttinger: blashyrk, I'm not sure atoms mean what you think they do

7:05 ,(atom {})

7:05 blashyrk: probably not :)

7:05 clojurebot: #<Atom@be611cf: {}>

7:05 blashyrk: I'm using cheshire to parse JSON

7:06 TEttinger: ,(def kinda-variable (atom {}))

7:06 clojurebot: #'sandbox/kinda-variable

7:06 blashyrk: but I want to make modifications to the hashmap I get as a result

7:06 TEttinger: ,(swap! kinda-variable assoc :a 1)

7:06 clojurebot: {:a 1}

7:06 TEttinger: ,@kinda-variable

7:06 clojurebot: {:a 1}

7:06 TEttinger: you may want transients

7:06 blashyrk: yes, but is it possible to assoc an entire hashmap to the newly defined atom?

7:07 and if so wouldn't that be kinda expensive?

7:07 TEttinger: yeah

7:07 no

7:08 ,(def kinda-variable (atom (zipmap (range 1000) (map char (range 32 1032)))))

7:08 clojurebot: #'sandbox/kinda-variable

7:08 TEttinger: ,@kinda-variable

7:08 clojurebot: {0 \space, 893 \Ν, 920 \θ, 558 \Ɏ, 453 \ǥ, ...}

7:08 TEttinger: unsorted

7:11 blashyrk: say I have a map in a var "some-map"

7:11 but it's a standard immutable map

7:11 TEttinger: blashyrk, the neat thing about clojure's data structures is that even though they are immutable, and cannot be modified after being created, if you make another, say, hashmap with most of its keys and values from an existing hashmap, the keys and values that are the same will be shared, not using additional memory and not requiring a copy

7:11 blashyrk: that sounds great, but what is the idiomatic way to construct an atomic map completely from an existing map?

7:12 TEttinger: so it isn't expensive to just return a modified version of that immutable map, and pass it to another fn expecting another map

7:12 the fn atom

7:12 (doc atom)

7:12 clojurebot: "([x] [x & options]); Creates and returns an Atom with an initial value of x and zero or more options (in any order): :meta metadata-map :validator validate-fn If metadata-map is supplied, it will become the metadata on the atom. validate-fn must be nil or a side-effect-free fn of one argument, which will be passed the intended new state on any state change. If the new state is unacceptable, the validate-fn should return false or thro

7:12 TEttinger: basically, you only care about the first part of that

7:13 the initial value of the atom will be the argument you pass to (atom)

7:13 blashyrk: I tried that already but it didn't work for some reason

7:13 let me check

7:13 ,(def some-map {:somekey 0, :someotherkey 2})

7:13 clojurebot: #'sandbox/some-map

7:14 blashyrk: ,(def mutable-map (atom some-map))

7:14 clojurebot: #'sandbox/mutable-map

7:14 blashyrk: ,@mutable-map

7:14 clojurebot: {:somekey 0, :someotherkey 2}

7:14 blashyrk: hmmm interesting

7:14 TEttinger: you still won't modify some-map

7:14 blashyrk: not sure why it didn't work for me

7:14 TEttinger: but mutable-map can be altered

7:15 ,(swap! mutable-map assoc :yetanotherkey [:whee :vecs])

7:15 clojurebot: {:somekey 0, :someotherkey 2, :yetanotherkey [:whee :vecs]}

7:16 blashyrk: ah I remember what failed in my case

7:16 can I use swap to basically assign?

7:16 like

7:17 ,(def mutable-map (atom {}))

7:17 clojurebot: #'sandbox/mutable-map

7:17 blashyrk: (swap! mutable-map ? some-map)

7:18 what would I use here to directly "assign" the original map to the atom?

7:18 or should I just redefine mutable map (after parsing the original map) ?

7:18 with def

7:21 oddcully: ,(doc reset!)

7:21 clojurebot: "([atom newval]); Sets the value of atom to newval without regard for the current value. Returns newval."

7:21 oddcully: ,(reset! mutable-map {:a 1})

7:21 clojurebot: {:a 1}

7:22 oddcully: ,(@mutable-map)

7:22 clojurebot: #error{:cause "Wrong number of args (0) passed to: PersistentArrayMap", :via [{:type clojure.lang.ArityException, :message "Wrong number of args (0) passed to: PersistentArrayMap", :at [clojure.lang.AFn throwArity "AFn.java" 429]}], :trace [[clojure.lang.AFn throwArity "AFn.java" 429] [clojure.lang.AFn invoke "AFn.java" 28] [sandbox$eval93 invoke "NO_SOURCE_FILE" -1] [clojure.lang.Compiler eval "C...

7:22 oddcully: clojurebot: stop confusing me!

7:22 clojurebot: Pardon?

7:22 oddcully: ,@mutable-map

7:22 clojurebot: {:a 1}

7:24 blashyrk: thanks!

7:24 opqdonut: ,(deref mutable-map) -- aka

7:24 clojurebot: {:a 1}

7:28 TEttinger: (inc oddcully)

7:28 lazybot: ⇒ 1

7:35 mavbozo: i put core.async/<! inside a try catch block the repl, I get this exception CompilerException java.lang.RuntimeException: Unable to resolve symbol: user in this context, compiling:(/tmp/form-init7054017936426169058.clj:1:1)

7:35 https://www.refheap.com/99395

7:36 weird, why it can not resolve the symbol of the namespace?

7:36 any suggestions on how to debug this problem?

7:39 TEttinger: that is odd, you didn't, say, paste an earlier line that started with user> did you?

7:41 mavbozo: TEttinger, no, i included the 'user>' prompt in the paste as a information that i was in namespace named 'user'

7:42 TEttinger: yeah, it's odd that it's not understanding the ns properly

7:42 very very strange

7:56 farzad: guys can i ask a dumb question? :D

7:56 TEttinger: no, there are no dumb questions

7:56 go right ahead :)

7:56 ~ask

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

7:57 farzad: ok how do i subclass a class like Class SimpleChannelInboundHandler<I>

7:57 that <I> thingie is puzzling me

7:58 TEttinger: it's a generic type, which actually isn't even used at the VM level. it's the same to clojure as SimpleChannelInboundHandler

7:59 a good example is the Java class ArrayList<T>, which usually contains one type specified by T. you could have, say, ArrayList<Point>

7:59 but clojure doesn't care, because the <T> is erased after compilation anyway, and is only used by Java to make sure that the ArrayList is what it thinks it is

7:59 farzad: well i subclass that with proxy and i get a class cast exception :/

8:00 TEttinger: (doc proxy)

8:00 clojurebot: "([class-and-interfaces args & fs]); class-and-interfaces - a vector of class names args - a (possibly empty) vector of arguments to the superclass constructor. f => (name [params*] body) or (name ([params*] body) ([params+] body) ...) Expands to code which creates a instance of a proxy class that implements the named class/interface(s) by calling the supplied fns. A single class, if provided, must be first. If not provided it default

8:00 TEttinger: this is a tough docstring...

8:00 farzad: lemme read that brb

8:02 TEttinger: ,(.add (proxy [ArrayList] [] (add [o] 5)))

8:02 clojurebot: #error{:cause "Can't resolve: ArrayList", :via [{:type clojure.lang.Compiler$CompilerException, :message "java.lang.Exception: Can't resolve: ArrayList, compiling:(NO_SOURCE_FILE:0:0)", :at [clojure.lang.Compiler macroexpand1 "Compiler.java" 6636]} {:type java.lang.Exception, :message "Can't resolve: ArrayList", :at [clojure.core$proxy$fn__5680 invoke "core_proxy.clj" 329]}], :trace [[clojure.core...

8:02 TEttinger: ,(.add (proxy [java.util.ArrayList] [] (add [o] 5)))

8:02 clojurebot: #error{:cause "No matching field found: add for class sandbox.proxy$java.util.ArrayList$ff19274a", :via [{:type java.lang.IllegalArgumentException, :message "No matching field found: add for class sandbox.proxy$java.util.ArrayList$ff19274a", :at [clojure.lang.Reflector getInstanceField "Reflector.java" 271]}], :trace [[clojure.lang.Reflector getInstanceField "Reflector.java" 271] [clojure.lang.Ref...

8:02 TEttinger: I uh don't use proxy much

8:03 farzad: well nothing new in the doc

8:03 i use proxy for other thing with no problem

8:03 things*

8:03 TEttinger: ,(.add (proxy [java.util.ArrayList] [] (add [this o] 5)))

8:03 clojurebot: #error{:cause "No matching field found: add for class sandbox.proxy$java.util.ArrayList$ff19274a", :via [{:type java.lang.IllegalArgumentException, :message "No matching field found: add for class sandbox.proxy$java.util.ArrayList$ff19274a", :at [clojure.lang.Reflector getInstanceField "Reflector.java" 271]}], :trace [[clojure.lang.Reflector getInstanceField "Reflector.java" 271] [clojure.lang.Ref...

8:03 TEttinger: hm

8:04 you don't include the <I> just so we're clear

8:04 farzad: yup

8:05 this is the proxy (defn get-handler []

8:05 (proxy [io.netty.channel.ChannelHandler] []

8:05 (channelRead [context msg]

8:05 (alert "Channel Read:" "")

8:05 (.close context))

8:05 (exceptionCaught [context cause]

8:05 (println "Exception Caught:" context cause)

8:05 (.close context))))

8:05 Empperi: doesn't proxy methods always have to have "this" as the first argument?

8:06 which effectively references the proxy object itself

8:06 farzad: no its there by default

8:06 Empperi: I'm pretty sure it isn't

8:06 not 100% though

8:06 farzad: Each method fn takes an additional implicit

8:06 first arg, which is bound to 'this.

8:06 read the doc

8:07 hyPiRion: ,(= (proxy [Object] (equals [this that] false)) 1)

8:07 clojurebot: #error{:cause "Unable to resolve symbol: equals in this context", :via [{:type clojure.lang.Compiler$CompilerException, :message "java.lang.RuntimeException: Unable to resolve symbol: equals in this context, compiling:(NO_SOURCE_PATH:0:0)", :at [clojure.lang.Compiler analyze "Compiler.java" 6535]} {:type java.lang.RuntimeException, :message "Unable to resolve symbol: equals in this context", :at [...

8:07 * hyPiRion grabs more coffee

8:08 farzad: you need to put [] after [Object]

8:08 for empty param

8:08 hyPiRion: ,(= (proxy [Object] [] (equals [that] true)) 1)

8:08 clojurebot: true

8:09 hyPiRion: well there you go

8:09 ,(= (proxy [Object] [] (equals [that] (prn this) true)) 1)

8:09 clojurebot: #object[sandbox.proxy$java.lang.Object$ff19274a "sandbox.proxy$java.lang.Object$ff19274a@5a7bfbc6"]\ntrue

8:10 TEttinger: wow nice

8:11 hyPiRion: this is the answer to almost every 4clojure problem

8:13 farzad: so any ideas why i get that cast exception?

8:20 TEttinger: hyPiRion, that's evil

8:20 "you just redefine equality so you're always right, classic political computer science"

8:20 it depends on what the meaning of the word is... is

8:36 wow. https://twitter.com/servertastic/status/586113649766494208

8:36 justin_smith: haha

8:37 mavbozo: i think i got disconnected

8:37 TEttinger, i found the source of the problem. but still, very strange

8:37 it's the ordering in :dependencies for clojure.core.async and pedestal-service. the problem happens if i put pedestal-service before clojure.core.async

8:37 TEttinger: woah, wow

8:37 how did you find that? nice work

8:39 justin_smith: go blocks are weird

8:39 TEttinger: (inc mavbozo) ; for the independent spirit that keeps clojure going

8:39 lazybot: ⇒ 5

8:39 mavbozo: bisecting the dependencies vector and removing each part til i the problem gone

8:41 even writing [io.pedestal/pedestal.service "0.4.0-SNAPSHOT" :exclusions [[org.clojure/core.async]]] still doesn't remove the problem

8:41 justin_smith: mavbozo: that vector nesting is too deep (has to do with a weird bug in lein deps :tree output that it would even suggest such a thing)

8:42 ragge: mavbozo: did you diff output of `lein deps :tree` with/without problematic dep order?

8:42 bordatoue: anyone experienced any issue with GC pauses, due to vmop taking long time

8:44 mavbozo: justin_smith, fixed it like this [io.pedestal/pedestal.service "0.4.0-SNAPSHOT" :exclusions [org.clojure/core.async]]

8:44 justin_smith, and the problem still happens

8:44 justin_smith: mavbozo: oh, OK

8:45 mavbozo: ragge, ok, i'll try to diff those

8:46 ragge: mavbozo: this can also be useful: `lein classpath | tr ':' '\n' | sort`

8:47 to see what actually ends up on your classpath

9:03 mavbozo: ragge, here is the result from my terminal on diffing the problematic deps with non-problematic deps

9:03 https://www.refheap.com/99396

9:04 looks like the difference is only the version of org.clojure/tools.analyzer.jvm

9:05 farzad: anyone see something wrong with this error message? "Cannot cast io.netty.channel.ChannelInboundHandlerAdapter to [Lio.netty.channel.ChannelHandler;"

9:05 that [Lio part bugs me

9:06 justin_smith: [Lio.netty.channel.ChannelHandler; is how the jvm says io.netty.channel.ChannelHandler[]

9:06 it means the same thing

9:06 ,(type (double-array [0.0]))

9:06 clojurebot: [D

9:06 farzad: i see thanks

9:06 justin_smith: ,(type (into-array ["hello" "world"]))

9:06 clojurebot: [Ljava.lang.String;

9:07 justin_smith: so anyway, it's getting a ChannelInboundHandlerAdapter where it expects to get an array of ChannelHandlers

9:07 you may need to use into-array? perhaps it was a varargs method?

9:08 you need to create arrays to use varargs

9:08 farzad: oh... let me check that

9:08 justin_smith: if that's the case, the example with the strings above is how you do that

9:08 it takes an optional type argument

9:09 ,(type (into-array Number [0.0]))

9:09 clojurebot: [Ljava.lang.Number;

9:09 farzad: yes! thank you!

9:10 justin_smith: np - this is another one to add to my "why did my clojure code broke" blog

9:10 (which isn't up yet)

9:11 farzad: XD

9:11 the java code is this: ch.pipeline().addLast(new TimeClientHandler());

9:11 justin_smith: ahah, and addLast was varargs

9:11 farzad: why doesn't that throw cast exception?

9:12 oh i see

9:12 justin_smith: because javac turns the varargs into a list automatically

9:12 the clojure interop does not do this

9:13 farzad: damn, should have checked the addLast doc...

9:13 wasted a nice couple of hours on this

9:13 justin_smith: farzad: well now, you know that errors mentioning that it expects Lfoo; means it was probably varargs (or at the very least it wanted an array)

9:14 I mean "now you know"

9:14 farzad: yep thanks again man!

9:14 justin_smith: farzad: and that's why I need to put this oral tradition from #clojure in a blog

9:14 so many "simple" things that you wouldn't really figure out on your own

9:15 mavbozo: like dependencies ordering problem

9:15 justin_smith: haha, that too

9:16 blashyrk: can someone who's willing to help me regarding changing a map (mutability) please send me a private message? thanks

9:16 justin_smith: clojure hash-maps are not mutable

9:17 at least not in any sane manner

9:17 farzad: justin_smith: if you ever start that blog, post it to /r/clojure maybe they put it next to the must read links XD

9:17 justin_smith: farzad: yeah, I'll be sure to put out the signal once it's ready

9:17 I really need to get off my lazy ass and do more writing for that actually

9:19 blashyrk: @justin_smith I can see that, but I'd appreciate any help (even doing it in an utterly 'insane' way). I'm about to start pulling my hair out

9:21 justin_smith: blashyrk: clojure is a functional language, the point of the language is that we can do these things without mutation, maybe if you give a small example of what you are trying to do we can help you. It's an actual proven scientific thing that anything that can be calculated via mutation can be calculated without mutation using recursion

9:21 "scientific" as in there are CS proofs of this

9:22 blashyrk: Ok, I'll put up a pastebin

9:30 dnolen: blashyrk: I don't think you need to paste anything yet. If you need state why are you not putting your map in an atom?

9:31 blashyrk: I don't explicitly need state

9:31 I just need to alter the map once like this

9:31 http://pastebin.com/cCfRMT7J

9:31 this is the fictional map

9:31 justin_smith: dnolen: he just wants to merge some maps

9:31 dnolen: he doesn't need mutation at all

9:31 blashyrk: what I want to do is fill the "defaults" onto each "model"

9:31 dnolen: blashyrk: then you don't need to mutate the map

9:32 ,(merge {:foo 1} {:bar 2})

9:32 clojurebot: {:foo 1, :bar 2}

9:32 blashyrk: meaning if :something or :something2 are omitted in the "model" map, they should be added

9:32 justin_smith: blashyrk: yes, that's what merge does

9:32 blashyrk: yes, but I also need some aditional logic in case a default value is overriden

9:33 dnolen: blashyrk: so write that logic

9:34 blashyrk: let me see

9:39 so how could I merge those that aren't overriden in the models and do something else for those that are?

9:40 dnolen: blashyrk: there are lot of ways to do this

9:40 blashyrk: also it seems that merge would automatically overwrite the values under the same keys

9:41 dnolen: you know the keys in the default map, you can get these keys, you can get the user supplied options keys

9:41 you can set difference to figure out what precisely is going to be supplied by the user

9:41 justin_smith: blashyrk: merge goes in a predictable direction

9:42 dnolen: ,(merge {:default 'default} {:default 'custom})

9:42 clojurebot: {:default custom}

9:42 justin_smith: blashyrk: the keys from a later argument in the list will not be replaced by keys earlier in the list

9:42 dnolen: ,(merge {:default 'default} {:other 'foo})

9:42 clojurebot: {:default default, :other foo}

9:42 blashyrk: alright, that's one part of the problem solved :)

9:43 thanks guys

9:43 for the other part of the problem, let's keep it simple and say I just want to concatenate the two entries under the same key

9:43 justin_smith: blashyrk: merge-with

9:44 dnolen: ,(merge-with concat {:foo [1]} {:foo [2]})

9:44 clojurebot: {:foo (1 2)}

9:44 blashyrk: ah

9:44 so the entire problem turned out to be a two-liner

9:44 thanks a lot

9:45 I have still a ways to go in terms of transferring my mindset from imperative to functional programming

9:45 justin_smith: it's worth it :)

9:46 the only drawback is that some things really do require imperative programming for pragmatic reasons, and you won't enjoy going back

9:46 puredanger: Bronsa: yt?

9:46 Bronsa: puredanger: yes, hi

9:46 puredanger: hey, I looked through your changes and they look good

9:47 nice catch in that one read-tagged* case too :)

9:48 I was just reading through the cljs change dnolen is working on and one thing I really don't like poking through over there is the knowledge of pending-forms

9:48 Bronsa: puredanger: what do you think about the TaggedLiteral/ReaderConditional change?

9:48 puredanger: that's fine

9:49 the pending-forms stuff is purely an implementation detail and something that we've discussed alternative solutions for

9:49 Bronsa: right, I saw that in the cljs code too

9:49 puredanger: in the clojure one, the read arities that take that are private

9:50 do you think we could pull the read arities that take pending-forms into a different private function?

9:50 read-internal or something?

9:50 we could either add an arity or cljs could call into the existing opts-only one - I've already recommended he do that

9:50 Bronsa: puredanger dnolen I don't think cljs needs to use that arity though, the opts+stream arity should be fine

9:50 puredanger: yes, that's what I'd recommend too

9:51 but I don't want to expose the pending-forms ones so other people might use them

9:51 Bronsa: puredanger: current workaround is to inject custom arglists https://github.com/clojure/tools.reader/commit/eb5153cbaec849e726cdac0202c446380a21e19a#diff-c18d0a09dfeefd43739ea3593cf209d2R881

9:52 moving the impl to a private read-internal is reasonable though

9:52 puredanger: seems easier to move those to private fn

9:52 (the alternate impl we've talked about is having a reader that could wrap the pbr and allow you to pushback whole forms instead, rather than keeping the side buffer)

9:52 we still might do that in the clojure one, but we didn't want to gate on getting around to that

9:55 ragge: mavbozo: sorry went away for a while. difference between tools.analyzer versions is huge (0.6.5 vs. 0.1.0-beta12) so I wouldn't be surprised if things expecting 0.6.5 would break

9:56 things like the core.async go macro, for instance

9:56 dnolen: puredanger: Bronsa: what does it need to look like, two arg read does not work

9:57 mavbozo: ragge, at least my problem gone when I exlude tools.analyzer from pedestal-service and then put core.async after pedestal

9:57 like this, https://www.refheap.com/99401

9:57 Bronsa: dnolen: (read {:eof eof-sentinel} pbr) should work

9:58 mavbozo: ragge, might be related to this http://dev.clojure.org/jira/browse/ASYNC-86

9:58 Bronsa: mavbozo: next core.async release will be t.a 0.6.5 compatible

9:58 dnolen: Bronsa: strange to flip the order for arity 2 but ok

9:58 Bronsa: dnolen: that's how c.c/read does it

9:58 dnolen: erg

9:58 puredanger: Bronsa: dnolen rich requested that for partial-ing

9:59 mavbozo: (inc ragge) ; for pointing the way to find which dependency which causes the problem

9:59 lazybot: ⇒ 1

9:59 puredanger: mavbozo: that change has not yet been released in core.async

10:02 dnolen: puredanger: Bronsa: done

10:05 acron^: noob question?

10:05 how would I swap! using cons

10:06 I can't seem to get the syntax quite right

10:06 justin_smith: ,(let [a (atom 1)] (swap! a cons '(2 3)))

10:06 Bronsa: dnolen: puredanger merging, will cut 0.9.0 in a couple of minutes

10:06 clojurebot: (1 2 3)

10:06 acron^: using conj: (swap! my-atom conj new-value)

10:07 justin_smith: acron^: not particularly useful - cons requires the thing to be added as the first arg

10:08 acron^: hmm, i suppose i could just use reverse...

10:08 justin_smith: why reverse?

10:08 if the collection is not a vector, conj will do the what you expect

10:08 just make sure you have the collection type with the behavior you require

10:09 acron^: i want the item adding at the front of the collection, not the end

10:09 that's all

10:09 justin_smith: ,(let [a (atom '(1))] (swap! a conj 0))

10:09 clojurebot: (0 1)

10:09 justin_smith: acron^: right, like I said, if you have the right collection type, conj will do what you want

10:09 acron^: interesting

10:10 justin_smith: acron^: conj is meant to add in the most efficient place

10:10 which is different for different collection types

10:10 acron^: I see

10:11 Are there any implicit differences for using a list over a vector?

10:11 when using*

10:11 justin_smith: lists are not good for indexed lookup

10:11 acron^: ok

10:11 that's fine

10:11 for my use case

10:11 thanks justin!

10:11 justin_smith: np

10:31 m4farrel: is there a function like scan in clojure? i.e. reduce with a running total: (scan + 0 [1 2 3]) => [0 1 3 6] ?

10:31 justin_smith: m4farrel: reductions

10:31 ,(reductions + 0 [1 2 3])

10:31 clojurebot: (0 1 3 6)

10:32 m4farrel: justin_smith: thanks

10:52 tap: Hi there, quick question. I want to gen-class that similar to this class https://github.com/properssl/java-jdbc-postgresql/blob/master/src/main/java/org/properssl/SingleCertValidatingFactory.java. What's the equivalent expression to `_factory = ...` in clojure?

10:53 Modifying protected member of a super class

10:53 justin_smith: well, initializing right?

10:54 anyway, I think you would want reify or proxy, and inside that you can establish the values of fields

10:54 reify if WrappedFactory is an interface, proxy otherwise

10:57 tap: Ok. WrappedFactory is an abstract class. Thanks for pointing out that I shouldn't use gen-class here. Good to know that I was learning the wrong thing for this.

10:57 Need to reread a bit more about proxy

10:58 justin_smith: tap: it would be possible with gen-class, but gen-class usually isn't the easier way to do something - best reserved as the last resort before writing java code

11:02 tap: justin_smith: I got a (wrong) impression that proxy is for creating an object. What I want is a class to replace 'NonValidatingFactory' in `sslfactory=org.postgresql.ssl.NonValidatingFactory` . FYI, I'm refering from this document https://jdbc.postgresql.org/documentation/81/ssl-client.html

11:04 justin_smith: tap: oh, if you specifically need a class and not a class instance, then you I think you'll need gen-class

11:07 tap: justin_smith: Ok. So for gen-class, I should be able to set `_factory` from `init` function, right? I can't really find an example to do that.

11:07 justin_smith: tap: for your initial question, you want the :exposes key to gen-class if you are creating a field that must be visible

11:08 tap: justin_smith: please disregard my previous message, didn't saw your recent one.

11:09 justin_smith: also, see the :state key if you need to create a field

11:09 and :init for setting fields of the superclass

11:11 so if you need to set something protected on the parent, you create a setter with :exposes and then call it in your :init

11:11 tap: if you haven't seen it, conj.io is helpful in general http://conj.io/store/v1/org.clojure/clojure/1.7.0-alpha4/clj/clojure.core/gen-class/

11:15 ircxy: ,(Math/pow 1.4 1/3)

11:15 clojurebot: 1.1186889420813968

11:15 ircxy: ,(let [y 3] (Math/pow 1.4 1/y)) ; fails

11:15 clojurebot: #<NumberFormatException java.lang.NumberFormatException: Invalid number: 1/y>

11:15 tap: justin_smith: conj.io looks nice. I like the syntax highlight. Never heard about it before. For some reason, it never hit my search result

11:15 justin_smith: ircxy: x/y is not a function call, it is a syntax for literals

11:16 ircxy: you can't embed a symbol in that kind of numeric literal

11:16 ircxy: Is there a way to use a var in a fraction?

11:16 justin_smith: ircxy: it's the equivalent of (let [x 9] 1.x)

11:16 ircxy: sure, use the / function

11:17 tap: it's a relatively recent thing, but arrdem puts a lot of work into it

11:17 ,(let [y 3] (/ y))

11:17 clojurebot: 1/3

11:17 ircxy: justin_smith: right on! ,(/ 1 3)

11:17 justin_smith: ircxy: it's special cased so that (/ x) is the same as (/ 1 x)

11:17 which is the most frequent usage, so that's cool

11:18 ircxy: justin_smith: very cool,thanks!

11:18 justin_smith: ,(/ (/ 20))

11:18 clojurebot: 20N

11:18 justin_smith: :)

11:18 ,(iterate / 20)

11:18 clojurebot: (20 1/20 20N 1/20 20N ...)

11:19 tap: justin_smith: thanks for pointing out :exposes. I'll learn more about it tomorrow. It's late night here

11:27 dnolen: Clojure 0.0-3190 pre-release going out to Maven and available here https://github.com/clojure/clojurescript/releases/tag/r3190

11:27 includes conditional reading support, which touched many things

11:28 so needs some serious tire kicking. If no bad news, would like cut a proper release tomorrow

11:29 er s/Clojure/ClojureScript of course

11:29 saik0: Any book recommendations for somebody familiar with java and lisps (scheme in my case), nice and terse?

11:30 justin_smith: ~books

11:30 clojurebot: books is programming clojure

11:31 justin_smith: there's also "joy of clojure" which shows more common lisp influence and takes a more erudite approach

11:31 that is, it kind of expects you either know things its referencing or can go read up yourself

11:31 not that it's a dissertation or anything, just less beginner focused

11:32 saik0: justin_smith: thanks

11:32 tbaldridge: saik0: if you know Java and Scheme, I highly recommend Joy of Clojure...

11:32 justin_smith: yeah, it's a great book if you don't let it intimidate you :)

11:32 tbaldridge: like justin_smith said, it gets rather advanced rather quickly, but sounds like that's what you want/need

11:35 saik0: tbaldridge: right, I'm mostly concerned with poking Clojure's macros and concurrency/parallism. not a 5 chapter into to functional programming. sounds up my alley thanks both of you.

12:46 crazydiamond: Hi. Is there way to beautify (pretty-print) bunch of data in Clojure other than clojure/pprint.prrint? It fails for me, while println works well

12:47 justin_smith: crazydiamond: fails?

12:47 crazydiamond: are you trying to supply multiple arguments? the solution is to pprint each thing separately, or put them all in one vector or hash-map

12:48 crazydiamond: justin_smith, StackOverflow errr

12:48 *error

12:48 justin_smith: oh, wow

12:48 infinite lazyseq?

12:48 crazydiamond: justin_smith, may be, yes, something lazy

12:48 though unlikely infinite

12:49 may I... make all lazy stuff non-lazy?

12:49 must I postwalk or something? but with what func

12:49 justin_smith: if you fully realize the collection via walk/post-walk, do you get the same error?

12:49 or just walk/tree-seq

12:49 ,(require 'clojure.walk)

12:49 clojurebot: nil

12:50 justin_smith: ,(clojure.walk/tree-seq {:a 0 :b [1 2 3]})

12:50 clojurebot: #error{:cause "No such var: clojure.walk/tree-seq", :via [{:type clojure.lang.Compiler$CompilerException, :message "java.lang.RuntimeException: No such var: clojure.walk/tree-seq, compiling:(NO_SOURCE_PATH:0:0)", :at [clojure.lang.Compiler analyze "Compiler.java" 6535]} {:type java.lang.RuntimeException, :message "No such var: clojure.walk/tree-seq", :at [clojure.lang.Util runtimeException "Util.j...

12:50 justin_smith: err

12:50 ,(tree-seq {:a 0 :b [1 2 3]})

12:50 clojurebot: #error{:cause "Wrong number of args (1) passed to: core/tree-seq", :via [{:type clojure.lang.ArityException, :message "Wrong number of args (1) passed to: core/tree-seq", :at [clojure.lang.AFn throwArity "AFn.java" 429]}], :trace [[clojure.lang.AFn throwArity "AFn.java" 429] [clojure.lang.AFn invoke "AFn.java" 32] [sandbox$eval72 invoke "NO_SOURCE_FILE" -1] [clojure.lang.Compiler eval "Compiler.ja...

12:51 justin_smith: ,(tree-seq coll? seq {:a 0 :b [1 2 3]})

12:51 clojurebot: ({:a 0, :b [1 2 3]} [:a 0] :a 0 [:b [1 2 3]] ...)

12:51 crazydiamond: yep

12:51 justin_smith: yep as in you do get the same error?

12:51 crazydiamond: 1 sec

12:52 yes

12:52 justin_smith: right, so there is a problem with the code generating one of your lazy-seqs I think

12:53 crazydiamond: I was thinking to turn it into the str and parse

12:53 LOL

12:53 but I don't have anything like (range) though

12:54 justin_smith: concat? sometimes nested concat calls can lead to weirdness (especially if you had something chunked like range or a vector going in)

12:54 crazydiamond: uhm, a lot of vecs

12:55 hiredman: crazydiamond: have you tried fipp?

12:55 https://github.com/brandonbloom/fipp

12:55 crazydiamond: no

12:55 hiredman, thanks!

12:56 bbloom: crazydiamond: if fipp doesn't work for you, please file an issue w/ a minimal repo

12:57 crazydiamond: bbloom, hiredman works! excellent

13:00 noncom: can anyone confirm that when you add [selmer "0.8.2"] to your project.clj and then (:require [selmer.parser :as selmer]) in some ns of your project, you get an exception of classloading ?

13:00 justin_smith: (inc bbloom)

13:00 lazybot: ⇒ 54

13:17 noncom: nope, spotted

13:19 mavbozo: noncom, you found the culprit?

13:20 noncom: mavbozo: yes, had to manually delete the "bin" and "target" folders..

13:33 michaler`: suddenly cursive went crazy indenting defn and def

13:33 anyone else experience this?

13:33 I think it's related to the latest release which I've updated today

13:34 cfleming: ping

13:36 KKalem: You sure you don't have an open paren above somewhere?

13:39 michaler`: KKalem: pretty sure, it's also shown like that in the preview in the settings screen

13:40 can I downgrade to a previous version with cursive?

13:46 ah it's in the usersguid

13:46 guide

13:51 cljnoob: I have implemented a "frequencies" like function using reduce... I based it off the example listed on the reduce function documentation. http://pastie.org/10082946 My question is in regard to line 3: " (fn [hm char] (assoc hm char (inc (hm char 0))))". Why is 0 a second argument to the (hm char 0) function call?

13:52 justin_smith: cljnoob: ##({:a 1} :a 0)

13:52 lazybot: ⇒ 1

13:52 justin_smith: cljnoob: ##({} :a 0)

13:52 lazybot: ⇒ 0

13:52 justin_smith: (doc get)

13:52 clojurebot: "([map key] [map key not-found]); Returns the value mapped to key, not-found or nil if key not present."

13:53 cljnoob: ah, it's a default value. make sense

13:53 justin_smith: ,(update-in {} [\a] (fnil inc 0))

13:53 clojurebot: {\a 1}

13:53 justin_smith: another way to express that assoc

13:54 though fnil should be pulled out to a binding if this is a tight loop

13:54 iirc

13:54 michaler`: it didn't help

13:54 damn

13:55 cljnoob: justin_smith:i'll play around with it. Thanks!

13:55 justin_smith: michaler`: you're absolutely certain you don't have something in there that messes up the indentation? extra open parens?

13:55 or maybe some missing close paren

13:55 michaler`: justin_smith: nope, it happens in every file

13:55 justin_smith: weird

13:56 michaler`: maybe I accidently pressed some key combination that changed something in Intellij

13:56 justin_smith: michaler`: my attitude to IDEs in general is kind of like that ancient aliens guy "IDEs" (complete with crazy hair and waving hands)

13:57 michaler`: heh

13:57 justin_smith: your attitude to IDEs doesn't help me solve my problem ;)

13:58 KKalem: justin_smith, you need an IDE with an attitude~

13:58 justin_smith: clearly the IDE gods have cursed your intellij installation, and you need to delete some fetish file, or offer it a fresh dead chicken

13:58 andyf: michaler`: It does if you stop using IDEs :)

14:03 constl: Is there a way to evaluate an argument inside a string ?

14:04 justin_smith: constl: you can use (eval (read-string s)) but usually eval is the wrong thing

14:04 ~eval

14:04 clojurebot: eval is sometimes useful - but only sometimes

14:05 constl: Maybe "evaluate" was a wrong choice of word

14:05 hmm

14:06 justin_smith: constl: what do you want done? conversion of string to integers / symbols / keywords? read-string suffices for that

14:06 constl: (let [my-name "Konstantinos"] (print "Hello my-name !)

14:06 justin_smith: well, you don't even need to covnert anything for that

14:06 *convert

14:06 constl: I recal something that i have to put before my-name so that it prints the value instead of the literal

14:07 justin_smith: absolutely not

14:07 constl: no?

14:07 clojurebot: no is tufflax: there was a question somewhere in there, the answer

14:07 justin_smith: ,(let [my-name "Konstantinos"] (println "hello" my-name \!))

14:07 clojurebot: hello Konstantinos !\n

14:08 justin_smith: if you want the literal instead of the value, you can use '

14:08 ,(let [my-name "Konstantinos"] (println "hello" 'my-name \!))

14:08 clojurebot: hello my-name !\n

14:08 constl: justin_smith: Is there a way to use print with only one argument and have my-name somehow print the value?

14:09 justin_smith: you mean string interpolation?

14:09 we have format

14:09 or various templating libs I guess

14:10 ,(let [my-name "Konstantinos"] (println (format "hello %s" my-name)))

14:10 clojurebot: hello Konstantinos\n

14:11 justin_smith: something like eg. selmer will let you use "hello {{my-name}}" or something close to it

14:11 constl: Can you print the value by only using this form (print "Hello, <my-name-value> !")

14:11 justin_smith: constl: you'd need a templating lib for anything like that

14:12 not something immediately available in clojure.core

14:12 constl: I recall something like \" " or something like that

14:12 justin_smith: and no, you would not be able to do that with just print either

14:12 constl: that's just a way to put " in a string

14:12 and it still involves multiple args

14:13 ,(let [my-name "Konstantinos"] (println (str "hello " my-name)))

14:13 clojurebot: hello Konstantinos\n

14:13 justin_smith: there's always that, but that's not much different than just using multiple args to println

14:15 danielglauser: And multiple args to println will put spaces in there

14:15 ,(let [my-name "Konstantinos"] (println (str "hello " my-name)))

14:15 clojurebot: hello Konstantinos\n

14:15 danielglauser: ,(let [my-name "Konstantinos"] (println (str "hello " my-name "!")))

14:15 clojurebot: hello Konstantinos!\n

14:15 danielglauser: Doh! Third try...

14:16 ,(let [my-name "Konstantinos"] (println "hello" my-name "!")))

14:16 clojurebot: hello Konstantinos !\n

14:16 danielglauser: IMHO format is the idiomatic way to do what you are looking to do

14:18 constl: thanks for the help all

14:22 oddcully: ,(let [rep {"name" "World" "greeting" "Hello"}] (clojure.string/replace "<greeting> <name>" #"<(.*?)>" #(get rep (%1 1))))

14:22 clojurebot: "Hello World"

14:24 justin_smith: oddcully: nice

14:25 oddcully: constl: ^^ poor mans version of what you are after; yet i'd rather go for some template engine

14:28 cfleming: michaler`: Did you get that fixed?

14:43 michaler`: cfleming: hi

14:43 cfleming: no..

14:44 cfleming: I checked "default to indent only" as a temp workaround

14:44 cfleming: do you have an idea?

14:46 cfleming: michaler`: Are you getting a lot of unresolved symbols?

14:46 michaler`: cfleming: nope

14:46 cfleming: everything works as usual

14:46 cfleming: michaler`: Ok - are you using leiningen?

14:46 michaler`: yes

14:47 is there another way? :)

14:47 yes of course there is..

14:47 cfleming: Ok, is your lein project present in your Leiningen toolwindow?

14:47 michaler`: let me check

14:47 yes, it's there

14:47 as usual

14:47 cfleming: Hehe, yes - I don't use lein for Cursive, although I do use it to fetch the deps

14:48 Weird - so what's the problem that you're seeing?

14:48 michaler`: cfleming: would you like to do a remote session?

14:48 teamviewer?

14:49 cfleming: michaler`: I'm just about to head in to the office, it would have to be in about 30-45 mins

14:49 michaler`: cfleming: the problem is that although def/defn is set to ident only, they are formated as if they are set to 1

14:49 cfleming: ok, cool

14:51 cfleming: michaler`: Ok, if you got to Settings->Editor->Code Style->Clojure->Form parameters, what are def and defn set to?

14:51 michaler`: cfleming: indent

14:52 cfleming: michaler`: Well that is really weird. Is this problem when you hit enter while typing the form, or when you reformat the whole file?

14:52 michaler`: Is the code open or is it a work thing?

14:52 michaler`: Does this happen for other projects as well?

14:53 michaler`: shit

14:53 it's gone

14:54 sorry

14:54 err

14:54 cfleming: michaler`: The problem is gone?

14:54 michaler`: yes..

14:54 cfleming: Well that's good :-)

14:54 What I suspect has happened is that the indexes might have gotten in a funky state after the update

14:54 michaler`: i started checking if it happens only on reformat and then it's gone

14:55 * justin_smith waves his hands again "IDEs"

14:55 cfleming: Try File->Invalidate caches and restart, which will force them to be rebuilt

14:55 michaler`: cfleming: yes, it's possible.. I also had a dirty shutdown of the coputer after which intellij complained about some corrupted files

14:55 cfleming: Just ignore the crazy guy in the corner

14:55 justin_smith: :)

14:55 michaler`: cfleming: but it happened this morning..

14:56 cfleming: michaler`: Oh ok, I bet that was it then

14:57 michaler`: cfleming: ok, thanks

14:57 cfleming: when can I start paying you for Cursive? :)

14:58 cfleming: michaler`: Soon, I think - sometime after Clojure/West :)

14:58 timvisher: i _should_ be able to override `:uberjar-name` in a project.clj profile right?

14:59 michaler`: cfleming: ah, ok.. then I'll patiently wait ;)

15:00 mavbozo: timvisher, yep

15:00 michaler`: justin_smith: btw, I hope you are not suggesting emacs as the cure for IDEs..

15:00 timvisher: mavbozo: good :)

15:00 justin_smith: michaler`: oh, absolutely not

15:00 I'm just being a jackass :)

15:00 michaler`: heh

15:01 justin_smith: michaler`: cider has had issues that could compete with any IDE's worst

15:01 michaler`: yeah, it true.. and emacs is a nightmare on it's own..

15:02 amalloy: justin_smith confirmed trouble-maker

15:09 andyf: I'm not recommending it to anyone that wants IDE-like features, but emacs + clojure-mode is all I use

15:10 constl: Is there a way to get the number of arguments passed in a function with variadic arity?

15:11 justin_smith: constl: ##((fn [& args] (count args)) [1 2 3])

15:11 lazybot: ⇒ 1

15:11 justin_smith: constl: ##((fn [& args] (count args)) 1 2 3)

15:11 lazybot: ⇒ 3

15:14 constl: ##((fn [one two & args] (count args)) [1 2 3])

15:14 lazybot: clojure.lang.ArityException: Wrong number of args (1) passed to: sandbox6330/eval46602/fn--46603

15:16 ncthom91: hi all. Suppose I have a project wherein I define a simple value at the top of my file.. how can I override/mock that value during testing?

15:17 justin_smith: ncthom91: you can use with-redefs for that

15:17 ncthom91: also worth considering is writing your functions so that the concrete impl that would change at test vs. runtime is a parameter you explicitly pass in

15:18 and of course defining a trivial wrapper for typical usage

15:18 makes testing that much simpler

15:19 mavbozo: ncthom91, there are many ways of managing external dependencies for testing which you can read here http://blog.josephwilk.net/clojure/isolating-external-dependencies-in-clojure.html

15:19 justin_smith: eg. (defn printer [stream string] (.write stream string)) (def printit (partial printer *out*))

15:21 (inc mavbozo) ; good link, where'd you find it?

15:21 lazybot: ⇒ 6

15:23 mavbozo: i spent months studying ways to do dependency injection in clojure until i settle for stuart sierra's approach

15:24 i think a read almost blog posts

15:24 ncthom91: hmm cool ok

15:24 mavbozo: s/a/i/

15:27 hellofunk: if i have (map #(some-fn % (some-op foo bar)) coll) will the (some-op foo bar) run with each pass over a collection item, or does (some-op foo bar) evaluate once into the whole of the map fn?

15:28 Kowryh: what clojure resources would you guys recommend beside the obvious ones (e.g. the docs)

15:28 mavbozo: Kowryh, books

15:28 Kowryh: https://thechangelog.com/rich-hickeys-greatest-hits/ i've yet to watch these, though supposedly they're pretty good

15:29 mavbozo: Kowryh, infoq videos about clojure

15:29 justin_smith: hellofunk: easy to test ##(map #(cons % (println "check")) (range 10))

15:29 lazybot: ⇒ (check

15:29 justin_smith: oops

15:29 hellofunk: easy to test ##(map #(cons % (print "check")) (range 10))

15:29 lazybot: ⇒ (checkcheckcheckcheckcheckcheckcheckcheckcheckcheck(0) (1) (2) (3) (4) (5) (6) (7) (8) (9))

15:29 hellofunk: lazybot is rich! it just wrote a bunch of checks

15:30 justin_smith: also, that was some terrible nil punning, before you brought the regular punning in

15:30 Kowryh: mavbozo: specifically what book(s)?

15:30 if I were to read only one (for a start)

15:31 but thanks, I'll have to check those infoq videos out

15:31 mavbozo: Kowryh, depends on your previous experience with functional programming languages

15:31 Kowryh: well it's non-existent

15:31 (but yeah I should have mentioned that)

15:31 mavbozo: well, clojure programming or programming clojure

15:32 Kowryh: thanks, I'll look into those

15:39 ncthom91: what's the motivation behind putting *earmuffs* on your variable name?

15:40 sobel: ncthom91: keeps ears warm

15:40 ncthom91: :)

15:40 justin_smith: ,(def *t* 1)

15:40 tbaldrid_: ncthom91: that normally means that it's a dynamic var, and may be re-bound within a given scope

15:40 clojurebot: #error{:cause "denied", :via [{:type clojure.lang.Compiler$CompilerException, :message "java.lang.SecurityException: denied, compiling:(NO_SOURCE_PATH:0:0)", :at [clojure.lang.Compiler analyzeSeq "Compiler.java" 6732]} {:type java.lang.SecurityException, :message "denied", :at [clojurebot.sandbox$enable_security_manager$fn__835 invoke "sandbox.clj" 69]}], :trace [[clojurebot.sandbox$enable_securit...

15:41 ncthom91: tbaldrid_ so, convention?

15:41 sobel: also helps syntax highlighters find more important variables

15:41 ncthom91: definitely a convention

15:41 tbaldrid_: ncthom91: yeah, it's convection

15:41 justin_smith: ncthom91: the usual message for (def *t* 1) would be "Warning: *t* not declared dynamic and thus is not dynamically rebindable, but its name suggests otherwise."

15:41 ncthom91: would people use it to identify a variable intended for a with-redefs call in a test suite?

15:41 sobel: for example, yes

15:41 justin_smith: ncthom91: well, you wouldn't need with-redefs

15:41 tbaldrid_: althought, IIRC the compiler does warn you if you create a dynamic var without earmuffs

15:41 justin_smith: you could just use binding

15:42 sobel: or most of the with- family of fcns

15:42 justin_smith: tbaldrid_: yeah, the warning I was trying to provoke before, but I got bot weirdness instead

15:42 ncthom91: justin_smith oh, true

15:42 sobel: it's a common/useful pattern

15:42 ncthom91: and the ^:dynamic declaration enables binding right?

15:42 justin_smith: right, and it goes with earmuffs

15:43 sobel: i like that, earmuffs. i'm going to call the thread macro buttplug.

15:43 threadfirst

15:44 ncthom91: sobel lol what?? how did we get from earmuffs to buttplug

15:44 sobel: 'we' didn't

15:45 ncthom91: sorry, you :)

15:46 sobel: easily!

15:54 puredanger: tbaldrid_: doesn't it warn if you use earmuffs for a NON-dynamic var?

15:55 * tbaldrid_ checks to see what it does

15:55 puredanger: ,(def *foo* 1)

15:55 clojurebot: #error{:cause "denied", :via [{:type clojure.lang.Compiler$CompilerException, :message "java.lang.SecurityException: denied, compiling:(NO_SOURCE_PATH:0:0)", :at [clojure.lang.Compiler analyzeSeq "Compiler.java" 6732]} {:type java.lang.SecurityException, :message "denied", :at [clojurebot.sandbox$enable_security_manager$fn__835 invoke "sandbox.clj" 69]}], :trace [[clojurebot.sandbox$enable_securit...

15:55 justin_smith: puredanger: yeah, I tried to do that above

15:55 tbaldrid_: yep, it's the other way around

15:55 justin_smith: clojurebot is a weirdo

15:55 puredanger: sorry :)

15:55 tbaldrid_: puredanger: is right

15:55 eh... puredanger is right

15:56 justin_smith: ~puredanger is right.

15:56 clojurebot: Ik begrijp

15:56 mavbozo: ~earmuffs

15:56 clojurebot: Titim gan éirí ort.

16:26 akkad: in clojure is (sleep 200) the right format?

16:26 mavbozo: ,(doc sleep)

16:26 clojurebot: excusez-moi

16:27 justin_smith: akkad: (Thread/sleep 200)

16:27 for jvm clojure

16:27 it's an interop call

16:27 akkad: thanks

16:28 dnolen: Bronsa: ping

16:30 Bronsa: dnolen: pong

16:32 dnolen: Bronsa: so any reason (def arr #?(:cljs #js [1 2 3])) might not work?

16:33 Bronsa: nothing changed w/ how we bind *data-readers*, the above will read as (def arr [1 2 3])

16:33 andyf: dnolen: Doesn't splicing need slightly different syntax?

16:34 dnolen: andyf: this isn't a splicing question

16:35 Bronsa: dnolen: what's not working? I just tried this and it works as expected http://sprunge.us/AZGX?clj

16:35 dnolen: Bronsa: that isn't right

16:35 Bronsa: why?

16:35 clojurebot: why is the ram gone

16:35 the-kenny: dnolen: I have a complaint: Why do you always do awesome ClojureScript releases right when I don't have any time to dive into them?

16:37 dnolen: Bronsa: I mean I should see a JSValue in ClojureScript but getting a vector instead

16:37 Bronsa: ah

16:39 dnolen: uh yeah there's definitely a bug there

16:41 rurumate: Is there something similar to core.logic which produces a (sequence of) random value(s), instead of a list of all values that match the conditions? This would be very useful for unit testing.

16:41 dnolen: the-kenny: :), the hard work on this one was done by other people, can't take much credit for this one. Just had to make sure .cljc files can be consumed.

16:42 justin_smith: rurumate: maybe you want generators?

16:42 rurumate: I'll check it out thanks

16:42 justin_smith: rurumate: https://github.com/clojure/test.generative this is what I meant

16:43 rurumate: see also test.chuck from gfredericks

16:45 mavbozo: wow! a name from a tv series

16:45 rurumate: I'm looking for something that also generates csv data and strings that match a given pattern, like Xeger

16:45 justin_smith: rurumate: test.chuck can generate strings that match a regex

16:46 rurumate: so for instance you could say, I need a list of random vectors [a, b] where a is a number 0 <= a <= 10 and b is (* a 2)

16:46 justin_smith: yes, test.check can do that

16:47 mavbozo: (inc gfredericks)

16:47 lazybot: ⇒ 135

16:52 programisto: reatlively simple clojure problem here

16:52 https://gist.github.com/aaron-lebo/720345a76d08faad4b13

16:52 any help appreciated

16:53 *spelling

16:53 justin_smith: programisto: looks like you want something more like mapcat

16:53 rather than map

16:54 programisto: also, it would have worked if you used "apply concat" instead of just "concat"

16:54 but that's what mapcat is for, so just use that

16:54 programisto: justin_smith: thank you

16:54 exactly what i needed

16:54 justin_smith: ,(apply concat (map #(vector " " %1) [1 2]))

16:54 clojurebot: (" " 1 " " 2)

16:55 justin_smith: ,(mapcat #(list " " %) [1 2]))

16:55 clojurebot: (" " 1 " " 2)

16:55 justin_smith: latter is my preferred version, of course

16:55 programisto: yep it diefnitely worked

16:56 i figured something existed, couldn't find it via google

16:58 justin_smith: programisto: conj.io is really useful for this stuff

16:58 http://conj.io/

17:08 Bronsa: dnolen: should be fixed, can you try master? will release 0.9.1 once you confirm it works

17:08 dnolen: Bronsa: trying now

17:15 TimMc: justin_smith: Or just use fl-- *is hit by anvil*

17:15 justin_smith: (inc anvil)

17:15 lazybot: ⇒ 1

17:16 justin_smith: (inc TimMc)

17:16 lazybot: ⇒ 94

17:16 ntaylor: haah

17:19 dnolen: Bronsa: fixed

17:21 TimMc: (flatten 5) ;;= [1 1 1 1 1]

17:28 justin_smith: (flatten {:width 1 :height 1 :depth 1}) ;;= {:width 1 :height 1 :depth 0}

17:30 TimMc: (flatten "a") ;;= ?

17:30 \_

17:33 amalloy: TimMc: () would be my guess, but i forget

17:33 oh i just got here, you are in some weird fantasy land

17:33 TimMc: this should be no surprise

17:33 justin_smith: haha

17:34 TimMc: ,(flatten 5) ;; actually

17:34 clojurebot: ()

17:37 justin_smith: wow, even logicians have a lot of trouble with concurrency and mutability http://en.wikipedia.org/wiki/Yale_shooting_problem

17:38 dnolen: ClojureScript 0.0-3191 going out https://github.com/clojure/clojurescript/releases/tag/r3191, only change is tools.reader bump

17:39 TimMc: justin_smith: That's a terrible article.

17:39 it should feel bad

17:41 justin_smith: TimMc: yes, it's a badly written article

17:42 here's another writeup http://web.stanford.edu/~laurik/fsmbook/examples/YaleShooting.html

17:43 I think the clojure program demonstrating the concept would be pretty short. An agent would be a decent fit.

17:44 kwladyka: on http://www.clojure-toolbox.com/ there is about 15 web frameworks... why? Are they diffrent or really almost the same?

17:44 justin_smith: kwladyka: they are not all frameworks. For example Moustache is a templating language.

17:44 Some of them are defunct.

17:45 Actually, I think most of them on that list are defunct.

17:45 kwladyka: hmm

17:45 justin_smith: clojure-toolbox should really have a "last updated" date

17:46 kwladyka: i found some atricles, but with data 2014 or older... can you give me some names of web framework what should i be interested in?

17:46 justin_smith yes it should :)

17:46 justin_smith: kwladyka: most clojure web devs are not using a framework. There are a number of useful libs. Most of them are compatible with or even dependent on ring.

17:47 ring being the underlying web-server abstraction that is by far most popular

17:47 kwladyka: hmm so are suggesting to use only ring?

17:47 justin_smith: compojure is the most popular routing lib. You can do a lot with just compojure and ring (or switch to another router if you get tired of compojure)

17:48 kwladyka: so use ring + other libs working with ring if i need them?

17:48 justin_smith: kwladyka: compojure+ring is the most frequent combo I think, but after you get the basics, there are other routing libs too (if you need reverse routing, or dynamically generated routes for example)

17:48 right

17:48 xemdetia: since we are talking about ring is there any neat way to get some verbose build output for lein ring uberwar that I am not seeing?

17:48 justin_smith: kwladyka: lein new compojure some-name will create a working compojure app

17:49 xemdetia: not that I know of, are you having some issue with what uberwar does?

17:50 dnolen: Nice https://github.com/LonoCloud/synthread/blob/master/test/lonocloud/synthread/test.cljc

17:51 xemdetia: justin_smith, I brought nrepl into my project which is a slew of dependencies but it does not seem like it is ever finishing. I am not seeing io wait or java spinning

17:51 kwladyka: justin_smith i am thinking about architecture in Clojure. I am reallny not happy with MVC. I believe app should have separated core, UI and database, but.... i am not sure how to build architecure of folders, files, namespaces etc. in Clojure but i feel Clojure will give me power for that :) Can you recommend something to read with example of architecture?

17:52 justin_smith: in clojure we tend to do things less in terms of architecture and more in terms of well known operations on simple immutible data types

17:52 kwladyka: justin_smith i can use AngularJS, Clojure, DB it will be easier but if use only Clojure and DB putting HTML into code make me a little confuse how to do that in right way

17:52 justin_smith: this is a very broad generalization of course

17:52 TimMc: dnolen: Does that achieve having a common source file for clj and cljs?

17:53 justin_smith: xemdetia: you probably do something like (def server (nrepl/start-server)) and that starts a listening server when you compile

17:53 xemdetia: that would explain it

17:53 justin_smith: xemdetia: that's a blind guess, but that's a common source of that problem

17:53 xemdetia: no the project is pretty much just that. I just want a nice way to shim my tomcats

17:54 cfleming: ping puredanger

17:54 puredanger: yo

17:54 justin_smith: xemdetia: if that is the issue, the fix is to have a delay for the nrepl server, that gets forced in -main

17:54 xemdetia: or a promise that gets delivered if you need parameterization or anything

17:55 xemdetia: or a dumb route get request to switch it on

17:55 puredanger: cfleming: question?

17:55 justin_smith: xemdetia: sure, sure

17:55 cfleming: puredanger: I have a reader conditional question: currently I can't see a way that "send top form to REPL" will work with them, since the REPL doesn't know the file type the form came from.

17:55 justin_smith: xemdetia: but that's compatible with a dleay

17:55 xemdetia: idea being it's simpler to know that there is only one thing this def could be (even if it isn't realized until later)

17:56 puredanger: cfleming: that was raised by someone on the google group

17:56 justin_smith: compare with doing def at runtime, or using a mutable datatype - much more complex

17:56 puredanger: cfleming: I talked to Rich about it briefly and we're considering some way to be able to make the repl understand that

17:56 cfleming: puredanger: It was? I didn't see that - I'll check.

17:57 puredanger: it was a response on the alpha6 release iirc

17:57 cfleming: puredanger: Currently it will work in Clojure, because I'm using a horrible load-file hack to get the line numbering right

17:57 puredanger: But not cljs

17:57 xemdetia: true, I will have to examine that. I just was not identifying me trying to start the nrepl server as a problem when I issued the build job. I am glad I asked

17:58 puredanger: cfleming: the key of course is that repls choose how they read so making the repl more capable enables this (and other things)

17:58 cfleming: puredanger: Right, but this needs to be on a form-by-form basis

17:58 justin_smith: kwladyka: my usual solution is to have a template file (data, from the frontend guy) and a ring request (more data, from the client), and then use a rendering library (I use antlers, selmer is also good, there are other good ones) to synthesize those two data sources (plus maybe some input from clojure.java.jdbc) to make a page

17:59 puredanger: cfleming: in terms of the allowed features or just allowing read conditionals at all?

17:59 justin_smith: kwladyka: that page is put into a hash map, and returned to ring

17:59 kwladyka: justin_smith https://github.com/flyingmachine/brave-clojure-web is it good example? Do you know better?

18:00 justin_smith: kwladyka: is that even a clojure project?

18:00 cfleming: puredanger: Just allowing conditionals at all - currently they're not supposed to be allowed in clj files.

18:00 kwladyka: oh true... its about clojure but not in clojure...

18:00 sorry!

18:00 justin_smith: kwladyka: like I tried to mention above, if you run "lein new compojure my-app-name" that will create a working webapp from a template

18:00 and then you can start from there

18:01 puredanger: cfleming: you could programmatically invoke the reader based on the file type, then take the result and send that to the repl :)

18:02 kwladyka: i know but real example will help me to understand the process of code evolving :)

18:02 cfleming: puredanger: It wouldn't be the worst thing I've had to do :-)

18:02 justin_smith: kwladyka: I'd have something to share but sadly my good examples were client work, not open source

18:03 kwladyka: i understand :)

18:03 cfleming: puredanger: Like I say, this isn't a problem (for Cursive at least) for Clojure, but it is for CLJS

18:04 puredanger: CLJS can't use the load-file hack because load-file doesn't return the result of the last evaluation

18:04 kwladyka: the worst thing in my country (Poland) Clojure is not so popular and i can't find even in one of the biggest city Poznań Clojure programmers, we don't have any meeting groups or something like that. It is really hard to improve skills :)

18:05 cfleming: puredanger: Ah yes, I did see those mails but hadn't realised why he would want what he was asking for

18:05 justin_smith: kwladyka: euroclojure was in Krakow last year iirc

18:05 kwladyka: BTW: Are you close connected with Clojure? I see you are very active.

18:05 justin_smith: kwladyka: not to say that means anyone in Poland is using Clojure of course

18:06 kwladyka: I try to be pretty active, yeah, I am volunteering with clojurebridge next week to teach, and I'll be going to clojure/west in a couple weeks

18:07 kwladyka: thats good, i see Clojure community is very mature :)

18:10 cfleming: justin_smith: I'll be keen to hear about your clojurebridge experience, that's cool

18:10 justin_smith: I volunteer at our local coderdojo, teaching kids to program - mostly with code.org and Scratch

18:10 justin_smith: cfleming: thanks, I'll be happy to share

18:10 cool

18:10 cfleming: justin_smith: I'm really curious how people with no experience find Clojure

18:11 kwladyka: cfleming if you are asking about people with no functional programming experience i can tell you :)

18:12 justin_smith: cfleming: yeah, I think some of the women coming to clojurebridge don't even have programming experience. And my suspicion is it will be easier for them (compared to people with just a little programming experience)

18:13 maxmartin: justin_smith: don't forget to link to the event :) http://www.eventbrite.com/e/clojurebridge-workshop-for-women-in-software-tickets-16223350478

18:13 Looking forward to meeting you at the teacher training on Mondayh

18:13 amalloy: justin_smith: did you read the article about how learning haskell is not made easier by having no prior programming experience?

18:13 written by a student of a former loud member of #clojure

18:14 justin_smith: amalloy: I can guess who you mean, and no, I have not seen that

18:14 maxmartin: cool, see you there

18:14 kwladyka: justin_smith to be honest i feel Clojure is great, because i have even a lot business managin experience. Mayby even more then programming. In most cases i really didn't happy to put business thinking into code, but... but Clojure looks diffrent. I can use there my business thinking, thats what i need.

18:14 justin_smith: kwladyka: because it is declarative? the immutability?

18:15 cfleming: amalloy: It's probably made easier by having a math background more than anything

18:15 amalloy: justin_smith: https://superginbaby.wordpress.com/2014/11/18/learning-haskell-as-a-nonprogrammer/

18:16 cfleming: amalloy: She had an interesting post the other day about teaching her 10-year-old Haskell

18:16 justin_smith: amalloy: in all fairness, that reminds me of my experience trying to learn haskell too

18:16 amalloy: who is "she", cfleming?

18:17 kwladyka: justin_smith i am not sure why, but i think it is writing by functions not objects. And i don't have to care about data too much because it is immutability. I want focus on goal and have super redable code. But not readable like i can read for(...){..} and i understand operations. I want read code and uderstand as fast as possible business model and understand what is happening where.

18:17 cfleming: amalloy: The author of that blog post

18:17 amalloy: https://superginbaby.wordpress.com/2015/04/08/teaching-haskell-to-a-10-year-old-day-1/

18:18 kwladyka: justin_smith ofcoure it is also possible to write that readable in other langauges but other languages are not really matching with that, it like forcing them to do that.

18:18 but i am not a standard programmer, because i have really strong business skills and thinking in another way

18:19 justin_smith: amalloy: that was a really good and informative blog post, thanks

18:19 kwladyka: Clojure looks for me like something what i was looking for years :)

18:20 justin_smith: but I think it's less about haskell and more about the common sort of haskell intro that is out there - compare to eg. some of the excellent tabula-rasa intros to scheme

18:20 cfleming: justin_smith: I think the "what do I do with this?" is the biggest stumbling block for new programmers - starting by having something you want to achieve is vital. It's why games are such a great gateway drug for programmers.

18:21 justin_smith: cfleming: heh, yeah

18:21 cfleming: justin_smith: I think this particularly applies to Haskell since so little of the introductory stuff is aimed at actually making something useful.

18:21 justin_smith: cfleming: also, show a way things can go wrong without X before introducing X

18:21 before introducing the concept of a function, show how bad coding without functions is

18:21 etc.

18:21 cfleming: justin_smith: Yeah, no doubt - there's nothing like seeing the problem it solves to make you understand it and remember it.

18:22 justin_smith: but that would mean some stuff in haskell won't be teachable in your first few years :)

18:22 amalloy: justin_smith: i can't tell if you're saying that's a bad thing to do or a good thing

18:22 justin_smith: amalloy: which thing to do?

18:22 amalloy: "show a way things can go wrong without X before introducing X"

18:22 justin_smith: amalloy: oh, I think that's very important to do

18:23 kwladyka: from my point of view the most important thing is to show how easy you can write/read business model into code. This is the clue, beucase people writing code for business :)

18:27 ncthom91: hi all! How would I cast this ScriptObjectMirror to a normal map in clojure? http://cr.openjdk.java.net/~sundar/jdk.nashorn.api/8u40/javadoc/jdk/nashorn/api/scripting/ScriptObjectMirror.html

18:29 amalloy: there's no such thing as casting in clojure. banish it from your mind, and then you'll have room to think about what you actually need to do

18:29 justin_smith: ncthom91: (into {} (.entrySet S)) I think?

18:29 amalloy: justin_smith: i mean the thing is already a map

18:29 you can just call get on it

18:29 justin_smith: oh yeah, sure

18:29 dnolen: ncthom91: it implements Map, you don't need to convert it

18:29 amalloy: so it depends what ncthom91 actually want to do

18:29 justin_smith: good point

18:29 amalloy: dnolen: depends. ncthom91 might want to assoc into it, for example

18:30 dnolen: amalloy: right though if you're going to interact with ScriptObjectMirror probably not going to do that

18:31 ncthom91: amalloy justin_smith dnolen i see, thanks

18:43 another question :).. so I'm trying to instantiate a new Nashorn instance for each thread responsible for a given task. I'd like to test that my thread-local binding is working, so I wrote a test to evaluate a javascript expression in the nashorn engine in several threads: http://pastie.org/10083448

18:44 `lein test` exits before that println is ever hit... Is this a lazy evaluation issue?

18:44 amalloy: ~for

18:44 clojurebot: for is a beautiful person

18:44 ncthom91: ergh... adding a `doall` seemed to make it work

18:44 amalloy: clojurebot pls

18:44 ncthom91: guess it was

18:45 amalloy: tell me for is not a loop

18:45 justin_smith: ncthom91: use doseq

18:45 ncthom91: doseq is the same as for, syntax and execution wise, but without the laziness part

18:45 hiredman: ~for

18:45 clojurebot: for is not used often enough.

18:45 ncthom91: justin_smith cool, thanks

18:45 justin_smith: (and also it accepts arbitrary numbers of body expressions, because it's not lazy)

18:50 ncthom91: justin_smith so I took your tip and tweaked my test to actually test instead of just println: http://pastie.org/10083462

18:51 but `lein test` yields "Ran 0 tests containing 0 assertions."

18:51 can you help me with what that's about?

18:51 justin_smith: ncthom91: deftest is needed

18:51 ncthom91: omg >_< thanks, sorry for the dumb question

18:51 justin_smith: ncthom91: clojure.test doesn't know something is a test unless it has the :test metadata

18:52 ncthom91: also, line 8 could be (map deref futures)

18:52 oh wait, those aren't futures

18:52 they are just named "futures"

18:52 hehe

18:53 ncthom91: i borrowed that example from a blog post :P

18:53 justin_smith: no biggie

18:53 deref would work if they were actual futures

18:53 amalloy: justin_smith: are you sure they're not?

18:53 .invokeAll probably returns a List<Future>

18:53 justin_smith: amalloy: oh, good point

18:54 ncthom91: `(map deref futures)` here seems to work

18:55 amalloy: truly clojure is magic

18:55 justin_smith: awesome - amalloy was right, I forgot that this lower level syntax would return the same type as clojure's "future" would

18:55 (inc amalloy)

18:55 lazybot: ⇒ 256

18:55 amalloy: overflow! i should go reset my karma to 0

18:55 justin_smith: -256

18:55 kwladyka: what tools for clojure continous integration? jenkins or something else?

18:56 justin_smith: kwladyka: jenkins + lein works in my experience

18:56 kwladyka: thx

18:56 justin_smith: just make sure jenkins' lein setup is aproporiate, of course

18:57 kwladyka: ok its 1 a.m., its time to go sleep :) goodnight!

18:58 akkad: are there any emacs modes to highlight a dangling ]?

18:59 justin_smith: akkad: highlight-paren-mode should make it turn red when your cursor is on it

18:59 I think rainbow-parens should make it ugly permanently

19:00 akkad: thanks

19:00 first commit and poof, extra ]

19:01 justin_smith: akkad: also, "lein check" is good for finding goofs like that

19:02 and then "lein eastwood" for the stuff lein check doesn't find

19:07 ncthom91: if I've defined a new FixedThreadPool from the Executors class at the top of my file, is it a bad idea for threads to recursively call (.invokeAll) on that pool? The reference to the pool would be shared across the threads, so calling (.invokeAll) from within a thread's task would fill the same work queue as calling (.invokeAll) from the main thread, right?

19:16 justin_smith: ncthom91: yes

19:16 ncthom91: justin_smith yes it's a bad idea?

19:17 justin_smith: they are all using the same pool

19:17 are you wanting per-thread pools?

19:18 ncthom91: justin_smith nope. I'm building a graph recursively where I want each node assembled in a thread

19:18 so the recursive call is actually putting a task back in the thread pool

19:18 hmm that might be trouble because then threads are waiting on threads...

19:19 in a fixed thread pool

19:19 it's like a sophistocated deadlock

19:19 lol

19:19 justin_smith: ncthom91: a nice way to organize this is to have each thread reading tasks off a queue

19:19 and then putting tasks onto the queue (but not waiting on them)

19:20 core.async has nice abstractions for this stuff, but you can also use clojure.lang.PersistentQueue/EMPTY

19:20 or even a java.util.concurrent.BlockingQueue

19:20 I think that's the one

19:21 ncthom91: i'll have to find a nice way to prevent threads waiting on each other

19:22 justin_smith: ncthom91: for nested dependencies between threads, look into core.async

19:22 do channel writes / reads

19:22 it simplifies this stuff a lot

19:24 ncthom91: justin_smith any tips on mixing core.async and the Executors framework? I definitely want a fixed thread pool where threads stay alive

19:24 justin_smith: ncthom91: let core.async handle the threads

19:25 it uses a fixed pool, but you don't have to worry about the details in practice

19:25 it has a state machine where a given block might be dropped from one thread, and picked up by another later, but all the threads are utilized efficiently

19:26 ncthom91: justin_smith oh... it does use a fixed pool? Each of my threads needs its own Nashorn scriptengine instance to execute some javascript, and instantiating a new Nashorn engine takes like 500ms, so if threads are constantly spinning up and down i'll incur a lot of overhead

19:26 that's why I was intrigued by Executors' newFixedThreadPool

19:27 justin_smith: ncthom91: instead of giving a script engine to a thread, give it to a block of code that loops and executes things as needed

19:27 let core.async decide which actual thread is running your code

19:28 the result is that none of the engines are sharable between threads (each belongs to a single localized block of code), but threads can do other work while a given block is parked on eg. IO

19:28 ncthom91: justin_smith well the trouble is that Nashorn isn't thread safe yet, so I need to guarantee that each thread gets its own instance

19:28 oh.. hmm

19:28 justin_smith: no, you don't

19:28 the instances would only be accessible to one thread at a time

19:28 ncthom91: could you show me a quick example of how to do that?

19:29 justin_smith: (go-loop (let [task (<! task-chan)] (do-something task engine))

19:29 assuming engine was locally bound (likely in a surroundign let block)

19:30 the only code using the engine is that go-loop

19:30 I think that code has a few issues, but at least the basic idea is there I hope

19:30 ncthom91: and `go-loop` puts this off in a thread?

19:30 justin_smith: no

19:30 it creates a go block that loops

19:31 the go loop is run (when it is ready to be run (not parked on a channel or whatever))

19:31 it's picked up by whichever thread core.async decides to put it on

19:31 but that's implementation, in practice that part you don't really need to think about

19:32 you know that engine won't be used in other threads, because it is a local bindign that you don't share with other code

19:32 so at any time at most one thread can use it

19:33 ncthom91: interesting... how does core.async fan out the work then? Suppose that task queue that you <! from has like 10 tasks queued up

19:33 will core.async magically fan it out onto N threads?

19:33 justin_smith: ncthom91: first to read from the queue is first to get the task

19:33 ncthom91: no, if you want N engines working, start N go loops

19:34 all reading on the same channel

19:34 like delivery drivers waiting for the next order that's ready to go

19:34 each task is guaranteed to be taken by exactly one consumer

19:34 ncthom91: I see. So rather than bounding the thread pool I'm kind of bounding the "engine pool"

19:34 justin_smith: right

19:35 ncthom91: and letting core.async associate whatever threads it wants to the work

19:35 justin_smith: that is kind of inside out, huh :)

19:35 right

19:35 that's the model

19:35 it's a formal execution model called CSP

19:35 very well studied and well documented

19:35 concurrent sequential processes iirc

19:35 communicating sequential processes

19:35 sorry

19:36 ncthom91: So I could do something like (doseq [engine engines] (go-loop (let [task (<! queue)] (do-something task engine)))

19:36 justin_smith: exactly

19:36 ncthom91: which would associate a go-loop with each engine

19:36 interestingggg

19:36 justin_smith: I think you actually need a (recur) and a binding block in go-loop

19:37 but that's the idea, yeah

19:37 ncthom91: sure

19:37 justin_smith: it works out very nicely in practice

19:37 clean, simple

19:37 ncthom91: i believe it. It's totally not how I was thinking about this problem :)

19:37 justin_smith: the engines can each generate more tasks

19:37 without clogging the whole thing up

19:38 (unless they produce tasks faster than they consume them of course, nothing is going to help you with that :))

19:38 ncthom91: well the (do-something) function will be the one making more tasks, probably not the js engine, but I assume it's the same thing

19:38 justin_smith: right, sorry

19:38 sure

19:38 ncthom91: heh, it's a finite recursive problem, so it'll end eventually

19:38 justin_smith: cool

19:38 ncthom91: justin_smith are channels essentially queues in this model?

19:38 justin_smith: exactly

19:39 queues with special constraints

19:39 no peeking for example

19:39 ncthom91: so I can just create new tasks with (>! queue new-task)

19:39 justin_smith: exactly

19:39 the only way to check if a queue has data for you is to consume it (and remove it from the queue)

19:39 but the extra rules actually are needed for the overall provable correctness of the setup

19:41 ncthom91: how would just close one of those go-loops?

19:41 how would you*

19:41 justin_smith: ncthom91: if you close the input queue, it will return nil when they read

19:41 they can check for nil and exit

19:42 ncthom91: hm ok

19:42 justin_smith: and in fact they should :)

19:42 if you really needed fine grained control, you could give them individual channels, but that seems an odd choice

19:42 if you have to shut down one, I think it would make sense to shut them all down

19:42 ncthom91: no, I'm thinking this should work

19:42 yea

19:43 the only piece I have left to solve, conceptually, is that right now each node in my graph contains a pointer to its children nodes

19:43 so in constructing the graph, I need each child node before I can finish building the parent

19:43 justin_smith: ahh

19:43 ncthom91: that's how I ended up with threads relying on threads

19:43 justin_smith: ncthom91: adjacency list!

19:43 ncthom91: hahaha

19:43 exactly my thinking

19:44 it makes the traversal less fun to write, but I think that solves this problem

19:44 justin_smith: in all seriousness, when you have immutable data structures, adjacency lists are really the only sane way to do fully general graphs

19:44 the traversal only needs some minor adaptor code

19:44 ncthom91: right.. i was thinking references to atoms actually

19:44 but that seems unnecessarily messy

19:45 justin_smith: I'd say one atom, holding an adjacency list expressed as a hash-map

19:45 ncthom91: and the atom part there guarantees the thread safety?

19:45 justin_smith: right

19:45 using swap! for all modifications

19:45 it will retry if there are concurrent modifications

19:45 ncthom91: ok. This makes a lot of sense... probably a far easier approach than what I had in my head lol

19:46 justin_smith: you just need to watch out for side effects in the update function (big nono) and if you are hitting too many retries caused by concurrent updates, consider splitting it into sibling refs

19:46 ncthom91: by update function you mean teh function I pass to swap! ?

19:46 justin_smith: ncthom91: after doing a bunch of other ways of organizing recursive tasks on fully general (potentially cyclic) graphs, this is the sane way

19:47 yeah

19:47 ncthom91: what do you mean split into sibling refs?

19:47 split the map into pieces contained ins eparate atoms?

19:47 justin_smith: well, refs, not atoms

19:47 because refs are what you want for coordinated update

19:48 that relies on the state of multiple objects that are updated together

19:48 if you can do a hash map in an atom, that's simpler, but refs are simpler than multiple atoms

19:48 ncthom91: i see, ok

19:49 justin_smith: ncthom91: the docs on clojure.core are really good for describing the various concurrency primitives in clojure

19:50 ncthom91: i'm going to tackle it with a single hashmap in an atom at first

19:50 justin_smith: ncthom91: the chapters here on refs, agents, and atoms http://clojure.org/documentation

19:50 ncthom91: yeah, that makes sense

19:50 ncthom91: Realistically I won't hit a lot of concurrent updates unless I use a bunch of different js engines

19:51 but I won't know how many engines to use until I can measure the speed increase as I tweak that number

19:51 justin_smith: cool

19:51 ncthom91: justin_smith thanks so much. This is been immensely helpful

19:51 this has* been

19:52 justin_smith: if you want to dynamically tweak the count without restarting the system, you could assign each an ordinal integer, and send a message saying "everyone with a number higher than N, shut off"

19:52 ncthom91: your welcome, I think this stuff is really cool

19:53 ncthom91: justin_smith great! Cause I'm sure I'll be back with more questions soon enough hahaha

19:53 justin_smith: ncthom91: another possibility for the "sending a number" idea would be also to turn them on if the number is higher than their N

20:14 nicferrier: macros. can I read the current namespace for using inside a macro?

20:14 justin_smith: nicferrier: the ns invoking the macro is *ns*

20:15 nicferrier: so if I use *ns* I should be ok. I was using that outside macros and it was just "user"

20:15 of course, if I deliberately changed the namespace (like in a repl) it was correct

20:15 justin_smith: right, that's the namespace from which everything else is called

20:15 nicferrier: but then running outside the repl it was just user all the time.

20:15 justin_smith: but at expansion time, *ns* will be the ns in which the macro is expande

20:16 d

20:16 nicferrier: got it

20:16 thanks]

20:17 ncthom91: hey justin_smith one more question :). If I throw these tasks into the queue, and the go-loops spin and pick them up and update the adjacency list... how can I know get some notification of being "done"

20:17 where "done" is a state such that the queue is empty because the engines have finished all of the recursive updates to teh graph

20:18 justin_smith: oh, that's an interesting one

20:18 ncthom91: :)

20:19 hmm i suppose I could solve this problem differently

20:20 justin_smith: ncthom91: one possibility is an agent, that gets incremented for each task (including your initial tasks) and decremented when each one is fully processed (including queueing any follow up tasks), and use add-watch to do a special thing when the agent hits 0

20:21 because it can't hit 0 until there is truly no work remaining

20:21 since the number can't go up again if no task is still in progress

20:21 ncthom91: that's a cool idea

20:21 justin_smith: and it can't hit zero unless none are in progress

20:22 ncthom91: so my go-loop body could just `send` an `inc` call at the beginning and a `dec` call at the end yea? I'm going to read up on add-watch

20:22 justin_smith: ncthom91: I use a similar concept for testing task-pool abstractions, where I track the max value it hits, and also ensure that it gets back to 0

20:22 that showing the total max concurrency

20:23 ncthom91: yeah, that sounds right

20:23 I suggested an agent specifically because that won't retry (with retries it could hit weird states, better to not have to think about it)

20:24 ncthom91: justin_smith yea this sounds good, I like the approach

20:26 thanks!

20:29 darthdeus: if I have a map of vectors/sets, is there a simple way to add/remove items from a vector/set at a given key?

20:30 justin_smith: darthdeus: dissoc and assoc, but these don't replace your collection, they make a new one

20:30 darthdeus: justin_smith: yeah, but those work on the map

20:30 justin_smith: darthdeus: ahh, I mean update-in

20:30 darthdeus: oh cool, thanks :)

20:31 justin_smith: ,(update-in {:a [1 2 3]} [:a] pop)

20:31 clojurebot: {:a [1 2]}

20:31 justin_smith: ,(update-in {:a [1 2 3]} [:a] conj 42)

20:31 clojurebot: {:a [1 2 3 42]}

20:31 darthdeus: ,(update-in {} [:a] conj 42)

20:31 clojurebot: {:a (42)}

20:31 darthdeus: hmm

20:32 justin_smith: ,(update-in {} [:a] (fnil conj []) 42)

20:32 clojurebot: {:a [42]}

20:32 justin_smith: fnil there will act like conj if it exists, create a vector and then conj if it doesn't

20:32 darthdeus: interesting

20:33 still not sure if I like this about clojure, that modifying collections is so easy

20:33 justin_smith: darthdeus: nothing got modified

20:34 ,(let [a {:a 0}] (assoc a :b 1) a)

20:34 clojurebot: {:a 0}

20:34 justin_smith: you have to use the return value

20:34 darthdeus: yeah I know about persistent data structures

20:34 what I meant is that the APIs kinda make me want to use raw data strucutres

20:34 justin_smith: that's a good thing

20:34 that's the right way to do it in clojure

20:34 darthdeus: that depends ... coming from haskell it's very unnatural

20:34 justin_smith: darthdeus: clojure isn't haskell

20:35 darthdeus: I know, that's why I'm trying to do it this way and not the haskell way :)

20:35 justin_smith: also, you can use defrecord - it supports all hash-map ops, but is also a custom type

20:35 darthdeus: but it twists my brain a bit

20:35 justin_smith: right

20:35 defrecord is a great in between, where you get all the standard utility functions and power, but also get to paramaterize on type

20:35 for eg. protocols

20:36 darthdeus: hmm

20:37 I feel like I can hack together a lot of things fast ... which seems good ... though not really sure how this will work out long term

20:37 but hey, at least I'm getting work done :D

20:37 justin_smith: darthdeus: that is the clojure / haskell trade off in my experience

20:38 in clojure you get things done fast - but then might discover that you backed yourself into a corner by building so fast

20:38 with haskell, you don't know if you are making progress at all until ... BOOM it's done

20:38 as a vast overgeneralization of both languages, of course :)

20:38 darthdeus: indeed ... though true

20:39 at least from my very minor clojure experience

20:39 it feels that I almost don't have to know anything and it kinda works

20:39 like javascript, except a little better

20:39 justin_smith: only a little? :P

20:39 amalloy: clojurebot: clojure is like javascript, except a little better

20:39 clojurebot: Ack. Ack.

20:39 justin_smith: haha

20:39 darthdeus: well, maybe a lot better :D but still feels a lot like javascript at times

20:40 justin_smith: darthdeus: I think this heralds back partially to the javascript / scheme connection

20:42 darthdeus: might be

20:43 justin_smith: brendan eich: I was recruited to Netscape with the promise of “doing Scheme” in the browser... I’m happy that I chose Scheme-ish first-class functions and Self-ish (albeit singular) prototypes as the main ingredients.

20:44 "Whether that language should be Scheme was an open question, but Scheme was the bait I went for in joining Netscape."

20:46 interesting - if it weren't for the "looks like java" requirement, perl, python, tcl, and scheme would have been in the running

20:46 https://brendaneich.com/2008/04/popularity/

20:48 darthdeus: hehe

20:48 btw am I blind or are there no API docs for http-kit? http://www.http-kit.org/ I just can't find them anywhere in the repo or on the site

20:50 arrubin: darthdeus: Did you click Server or Client? Or did you want something more?

20:50 darthdeus: arrubin: well those only have examples, not exactly a description of all the functions and params etc

20:52 arrubin: I do not see anything else, so you might not be blind.

20:52 Or we might both be.

20:59 justin_smith: darthdeus: it's actually not a huge lib, the most concise docomentation is probably the code and doc strings in the github repo

21:00 darthdeus: I think these three files have all the info you might want https://github.com/http-kit/http-kit/tree/master/src/org/httpkit

21:00 and the impl details are hidden in the java source

21:01 https://github.com/http-kit/http-kit/tree/master/src/java/org/httpkit

21:02 darthdeus: cool, thanks

21:14 nuwanda_: ok, I think I'm going a little crazy. Is there a reason a repl would be able to load a ns with (require 'ns-name) but apparently fail to do (in-ns 'ns-name)?

21:15 justin_smith: nuwanda_: no, it's the opposite

21:15 require can fail if your ns has errors

21:15 in-ns doesn't really have a failure case

21:15 in-ns won't load your code though

21:16 nuwanda_: well, it doesn't actually fail, it just behaves as if the ns wasn't found

21:16 yeah, exactly, it doesn't load my code, while require seems to have no problem with it

21:16 justin_smith: nuwanda_: all in-ns is change the active ns, and optionally create a totally empty ns if it didn't exist

21:16 in-ns is not intended to load code

21:16 all it does is change which ns is active

21:16 it doesn't look for files or anything

21:17 even if you have a file implementing ns-name, it just creates an empty ns, ignoring that file

21:21 darthdeus: btw probably a dumb question, but can I expect things like sockets to behave properly if I put them in a Set?

21:22 justin_smith: darthdeus: if I understand you correctly, that would be yes, since they use object identity for equality

21:42 nuwanda_: (inc justin_smith)

21:42 lazybot: ⇒ 236

21:42 Shayanjm: So I've got something I need to run via a remote repl on a powerful server. It's going to take a long time, and obviously I don't want to lose the results in case something happens to the session

21:43 are there any 'clojure best practices' for dealing with this?

21:43 Or is it more or less the same sort of trouble shooting i.e: capture everything into a file?

21:49 sattvik: Shayanjm: It depends on what you need. You could perform the work in a future and stuff the result into a var somewhere (as an atom or a ref). If it needs to persist across JVM restarts, then, yes, you should store that somewhere designed for persistent storage.

21:50 Shayanjm: sattvik: if I run something in a remote repl and my ssh session dies (connection or something) in the middle

21:50 does the repl session persist?

21:50 No, right?

21:53 sattvik: No, it doesn't.

21:53 Shayanjm: Yeah so that's really my problem

21:53 Can't reasonably expect my session to stay live that long

21:55 sattvik: However, if you do something in a future, it will happen in a thread that's independent from the REPL. As such, you could save the future in a namespace somewhere so that you can later reconnect to your server and look it up.

21:55 Shayanjm: hmm interesting

22:04 sattvik: doesn't a future only exist for as long as a REPL session is active?

22:05 i.e: If I ssh into remote server, lein repl -> Run some stuff (in a future) that processes a ton of data and writes to a file

22:05 xphillyx: Hey all. Is there a way to memoize on a 2 argument function but say to only "key" on one of the arguments?

22:06 Shayanjm: and after I run that stuff the session dies. Wouldn't the REPL session die as well - meaning the future is no longer active & therefore stops running?

22:07 sattvik: or even more simply taking u

22:07 sattvik: No, the future runs a seperate thread. You do have to save it somewhere where you can look it up later, otherwise it will get garbage collected.

22:07 Shayanjm: sattvik: or even more simply taking out the write-to-file capability*

22:07 sattvik: Okay, so what if the future just returns a value. If I ssh back into the server & lein repl again, would the future still be referencable?

22:08 Despite the session ending before the future could return?

22:08 sattvik: So you can (def my-long-tasks (atom {})). In your REPL, you can (swap! my-long-tasks assoc :my-key (future (long-running-fn)))

22:08 Later, you can (:my-key @my-long-tasks).

22:09 Shayanjm: sattvik: but that would only work for as long as the REPL is active

22:09 the moment the ssh session dies, the repl would also die -- meaning my-long-tasks would start with a fresh empty value after lein repl is run again

22:10 sattvik: Nope. It's completely independent.

22:10 Shayanjm: Seriously? That doesn't seem right

22:10 sattvik: Well, if you do it in the user namespace, yes that may be the case.

22:10 Well, even then, the user namespace persists.

22:10 Shayanjm: sattvik: my set up is: I have core.clj running some long running processes inside of some clj project

22:11 but the namespace only persists for as long as the repl is active - and I thought it dies the moment the ssh session dies?

22:11 Hang on, there's an easy way to test this I think

22:11 justin_smith: Shayanjm: if you restart the vm, yes, all things you do in the repl are lost

22:12 Shayanjm: justin_smith: Not restarting the vm. If the ssh session dies (due to connectivity drop or hibernation or something)

22:12 would the remote repl session still persist?

22:13 or would it die with the ssh session? The machine is still live/running, the only thing that happened was that my laptop dc'd from the server

22:13 justin_smith: Shayanjm: it depends on what the OS decided to do. If you used nohup for example, it would not exit. Otherwise it gets an EOF, and a signal, and if you don't handle that signal, it will be shut down.

22:13 Shayanjm: Ah, so this is more an OS thing than anything else

22:13 any suggestions for how best to handle this on a remote Ubuntu machine?

22:13 justin_smith: yeah, the OS decides what happens when a remote user disconnects without shutting a program down

22:13 sattvik: The REPL session doesn't persist as such, but the namespaces don't just unload themselves. (Although the proposed new socket REPL works a bit differently)

22:14 justin_smith: Shayanjm: if you want the repl to stay running, use nohup

22:14 this is handy because it also make a log file

22:18 Shayanjm: hmm interesting

22:20 justin_smith: If I use nohup, run the process, and want to check on the value after x hours

22:20 how would I reconnect to that repl instance?

22:21 justin_smith: Shayanjm: lein repl :connect port

22:22 or any other repl client you like

22:22 in fact you can use the -L arg to create an ssh tunnel, and use your editor or ide repl client

22:22 Shayanjm: justin_smith: Oh so just standard lein repl :connect port?

22:23 justin_smith: yeah, or if you have a tunnel, you can even run the repl on your local box

22:23 with your preferred tools

22:23 and the jvm and actual repl instance are on the other host accepting connections

22:23 Shayanjm: ooh

22:23 and I can disconnect/reconnect at any time?

22:23 justin_smith: right, nrepl is nice like that

22:24 ncthom91: hey justin_smith if you're around, quick question: http://pastie.org/10083752

22:24 Shayanjm: game changer. How would I set this up on emacs cider?

22:24 ncthom91: this is a test for verifying the graph that we were talking about earlier gets updated properly

22:24 justin_smith: Shayanjm: instead of cider-jack-in just use cider

22:24 it will ask for a port

22:24 Shayanjm: oic

22:24 and point it to the remote IP + port?

22:24 ncthom91: but the tests inside the add-watch callback don't get run, it looks like

22:24 or at least they don't contribute to the test reporting

22:24 justin_smith: Shayanjm: no, since it's a tunnel, just the port suffices

22:25 Shayanjm: oh right because -L

22:26 justin_smith: Shayanjm: I think at this point even cider has options for making the remote connection, but I think it makes more sense to create the tunnel myself with ssh

22:26 maybe because I am old fashioned

22:26 Shayanjm: justin_smith: so workflow here would be: ssh with -L, run nohup lein repl, connect via cider, run stuff, disconnect (also disconnect tunnel?) -- reconnect at some later point via ssh tunnel & cider and check values?

22:26 justin_smith: yup

22:27 Shayanjm: That sounds fantastic

22:27 justin_smith: you can disconnect the tunnel if you like if you are not using it

22:27 Shayanjm: and I'm assuming I can just kill the repl with a standard (exit) despite nohup, yeah?

22:27 justin_smith: Shayanjm: it makes debugging prod so much easier

22:27 Shayanjm: from within the repl you can use (System/exit 0)

22:27 Shayanjm: is (exit) the same thing?

22:27 justin_smith: or any handy cider tool that does that for you, I guess

22:27 I have no idea what (exit) is

22:27 Shayanjm: Weird, my repl always starts and prompts me to use (exit) to exit lol

22:28 might've been some config magic when I first set up lein

22:28 justin_smith: Shayanjm: it's probably some nice feature I just never use

22:28 I just tried typing in exit (no parens) in my repl and it disconnected me

22:28 probably nrepl magic

22:28 haha

22:29 Shayanjm: hahaha that sounds about right

22:29 cool thanks a lot justin_smith, gonna try this

22:30 justin_smith: ncthom91: well, the test/is calls are not tests

22:30 they are assertions within a test

22:31 I don't think they do the right thing if they execute in another thread

22:31 usually what I do is create a "logging atom" and use swap! to poke that atom inside the async code

22:31 then I make assertions about the data in that atom

22:32 kevinwebster: Im trying to find out why Clojure has yet to support regex named capture groups. I dont see any discussion in JIRA or google groups about it. Anyone know why that is?

22:32 justin_smith: eg. have a cycle count, or a concurrency count that tracks its max value so far, etc.

22:32 kevinwebster: if java supports them, clojure does

22:32 ncthom91: justin_smith but how do you know when to make the assertions?

22:33 aperiodic: in om, when I have multiple om/build calls in the body of my om/IRender render implementation, it seems like only the last one is actually called

22:33 justin_smith: ncthom91: when the processes are done, at the end of that test

22:33 aperiodic: does anyone know why that happens?

22:35 justin_smith: kevinwebster: clojure directly uses java regexes, including the extended syntax http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html

22:35 kevinwebster: the docs there explicitly mention named capture groups

22:36 #"foo" is equivalent to Pattern.compile("foo")

22:39 ncthom91: justin_smith not sure what you mean. By the time i've hit the end of my test, the async code hasn't necessarily finished

22:40 justin_smith: ncthom91: then make sure you wait for it

22:40 ncthom91: each go loop returns a channel when constructed, when that channel returns a value on read, that means the loop has exited

22:40 yes, testing async things is weird

22:42 Shayanjm: justin_smith: nohup doesn't seem to be working, or I'm ignorant of how to even use it

22:43 on remote machine: nohup lein repl

22:43 justin_smith: yes

22:43 Shayanjm: No output, looks like it runs without failing

22:43 so A) I don't know the nrepl port

22:43 and B) If I try looking for a java process I don't see one

22:43 ps aux | grep java

22:43 justin_smith: Shayanjm: it creates a log file

22:43 nohup.out

22:43 if it is exiting, that's likely an issue

22:44 it should just sit there, not printing but also not ending

22:44 you can bg it safely

22:44 Shayanjm: oh gotcha

22:44 justin_smith: (Control-Z, bg, in most shells)

22:44 Shayanjm: does nohup.out get updated live?

22:44 or only on exit?

22:44 justin_smith: live

22:44 you can tail it

22:44 or whatever

22:44 Shayanjm: Okay, yeah there was an error running with nohup

22:44 but not regular lein repl

22:45 justin_smith: what was the error?

22:45 I usually am using lein run, and then starting a repl server from my -main

22:45 so maybe lein is being dumb about the tty here

22:45 Shayanjm: https://gist.github.com/shayanjm/15fad6a7c27c9127c461

22:46 justin_smith: yeah, lein is trying to do stuff with stdio that doesn't work in nohup

22:46 you're better off making an uberjar and using lein run anyway

22:46 err, not lein run

22:46 I mean java -jar, or you can use lein run instead of lein repl

22:46 and manually connect

22:47 Shayanjm: starting an nrepl server is a oneliner, example is in the readme https://github.com/clojure/tools.nrepl#embedding-nrepl-starting-a-server

22:47 Shayanjm: sweet, looking

22:49 justin_smith: what if instead of the repl, I just make -main execute everything that needs to be executed and prints to stdout?

22:49 uberjar that, nohup java -jar

22:49 justin_smith: Shayanjm: if that's all you need, sure, then you can just open up nohup.out

22:50 Shayanjm: yeah

22:50 justin_smith: or make a proper log file

22:50 if that's all you need, even better :)

22:50 Shayanjm: Yeah now that I think about it I don't really need to live-debug

22:50 because these are long running processes, they don't fail fast.

22:50 justin_smith: though sometimes being able to get into prod can be a lifesaver

22:50 Shayanjm: Yeah definitely

22:50 justin_smith: for figuring out a weird state interactively

22:50 Shayanjm: if I see that that's a benefit I'll definitely spin up a repl instance inside main

22:51 but this is mostly a data project

22:51 justin_smith: fair enough

22:51 Shayanjm: so there isn't a whole lot of state that needs to be managed

22:51 justin_smith: nice, sounds like you found the simple solution then

22:51 Shayanjm: this has been very enlightening though, I didn't know you could embed repl instances inside your applications

22:51 I could see that definitely coming in handy

22:52 justin_smith: yeah - definitely be careful though, an nrepl connection means full access to your program (and your program's user on that host)

22:52 Shayanjm: ah right

22:52 justin_smith: which is why it only accepts connections on localhost by default

22:52 which is mostly safe enough, since nobody uses multi user machines any more

22:53 Shayanjm: Right, so the 'actual' workflow for that would be to ssh in to the remote machine ahead of time

22:53 and run the repl with nohup

22:53 justin_smith: Shayanjm: right, like I was mentioning the ssh tunnel

22:53 Shayanjm: Yeah, and then ssh tunnel afterwards

22:53 with -L you can't run anything on the machine during that session, right?

22:53 justin_smith: no, it's a normal shell

22:54 Shayanjm: seriously?

22:54 gonna try 1 min

22:54 justin_smith: which is why I just make creating the tunnel part of the command when I ssh in

22:54 Shayanjm: so what's the syntax? somelocalport:remotehost:22? or localport:remotehost:remotereplport?

22:55 justin_smith: ssh -L localport:

22:55 since it's a thing that only listens on

22:55 or localhost whatever

22:55 my habit is using that "magic" ip

22:56 so you could optionally make a "hop" by connecting to yet another host (proxy tunnel) - ssh is very flexible

23:07 Shayanjm: justin_smith: I guess my confusion is: the remote port, should that be some random port or the nrepl port?

23:07 if it's the nrepl port, I would have to specify the port in the code -- I wouldn't be able to let lein generate that for me randomly

23:08 justin_smith: Shayanjm: the nrepl port. You can specify a specific port to nrepl

23:08 Shayanjm: you can?

23:08 justin_smith: right

23:08 Shayanjm: TIL

23:08 Oh

23:08 you mean when you spin it up inside main

23:08 justin_smith: there's a project.clj option

23:08 Shayanjm: ooh

23:08 justin_smith: :repl-options https://github.com/technomancy/leiningen/blob/master/sample.project.clj#L354

23:09 Shayanjm: awesome -- thanks for letting me pick your brain justin_smith

23:09 justin_smith: but yes, it's also easy in your own code, since it's a one liner

23:09 np

23:09 I know how many days one can waste on this stuff the first time

23:09 not because of not being capable, just not having a decent lead on what works

Logging service provided by n01se.net