#clojure log - May 01 2016

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

12:20 jonathanj: hmm, what the heck does this mean in the grand scheme of things: java.lang.ClassNotFoundException: clojure.java.shell, compiling:(documint/pdf.clj:53:10)

12:21 this only seems to happen when trying to build the uberjar

12:22 i'm calling (clojure.java.shell/sh ,,,)

12:22 do i need to (require) it first or something weird?

12:23 justin_smith: jonathanj: there's nothing weird here - no namespace is available that somebody hasn't required

12:23 jonathanj: and best practice is of course to require it in each ns where you use it (even though you could technically use it if anyone has required it if you use the full name)

12:24 jonathanj: okay, i guess that makes sense i just never paid attention when doing stuff like clojure.core/something or clojure.string/something but i guess something probably does require those already

12:25 justin_smith: right, in a bare repl not even clojure.string or clojure.set are available

12:25 though clojure.core is the one exception - without that you couldn't even require :)

12:25 jonathanj: is there a linter (or such) that could have caught this?

12:25 justin_smith: yes, eastwood

12:25 get it? linter eastwood

12:26 haha

12:26 I've tried quite a few, ://github.com/jonase/eastwood

12:26 err, bad paste

12:26 https://github.com/jonase/eastwood

12:29 jonathanj: thanks

12:29 justin_smith: even "lein check" might catch that (but might not)

12:29 jonathanj: yeah, in retrospect it's kind of obvious that this wouldn't have worked but i guess lein or boot must've been requiring it already so it didn't break in development

12:30 justin_smith: lein check just turns on reflection warnings then compiles every clj file

12:30 jonathanj: what's bad about reflection?

12:31 (lein check emits a ton of warnings for me)

12:31 justin_smith: jonathanj: makes code 100-1000x slower

12:31 jonathanj: mmm

12:31 justin_smith: (well, maybe only 10x slower sometimes, but it's pretty bad for perf)

12:32 jonathanj: good to know

12:33 _maddy: how do I get just the keys from a map?

12:33 justin_smith: _maddy: you're gonna laugh

12:33 ,(keys {:a 0 :b 1})

12:33 _maddy: no

12:33 clojurebot: (:a :b)

12:34 justin_smith: OK, you don't have to laugh

12:34 _maddy: thanks, seems to do the trick

12:35 kwladyka: _maddy keep always open http://clojure.org/api/cheatsheet - it is really treasure for knowledge

12:35 _maddy: now lets say I have a map {}, I need to "add" stuff to it, which I guess means to make a new one, but then the map-function returns something else, so do I always need to do (into {} ?

12:35 justin_smith: or use a function that knows how to return a hash-map, sure

12:36 (eg. reduce-kv)

12:36 kwladyka: _maddy try to use this cheatsheet, really, in long term condition you will be happy with that. Just read all function in map section

12:37 _maddy: kwladyka: thanks, bookmarked it

12:37 kwladyka: _maddy you will discover many interesting things

12:37 justin_smith: ,(reduce-kv (fn [m k v] (assoc m k (inc v))) {} {:a 0 :b 2 :c 42})

12:37 clojurebot: {:a 1, :b 3, :c 43}

12:37 justin_smith: matt_d: for third party libs, there is also specter

12:37 err

12:38 _maddy: ^

12:40 _maddy: justin_smith: I am adding a new key though, not altering the old ones, is that different?

12:40 luma: ,(assoc {:foo 1} :bar 2)

12:40 clojurebot: {:foo 1, :bar 2}

12:40 justin_smith: _maddy: shouldn't be - as long as the info to add keys is somewhere in the input

12:41 _maddy: since you mentioned mapping I thought you were traversing the hash-map

12:41 (which is what reduce-kv is designed for)

12:41 _maddy: not traversing really, just adding a key

12:41 justin_smith: oh, then yeah, just assoc is all you need

12:42 assoc is var-args too

12:42 ,(assoc {:a 0} :b 1 :c 2)

12:42 clojurebot: {:a 0, :b 1, :c 2}

12:47 kwladyka: is it possible to write in schema https://github.com/plumatic/schema map has to have :id or :code or both?

12:48 or maybe i am using not the right tool?

12:48 justin_smith: kwladyka: you mean it needs one of the two, and can optionally have one of the others?

12:48 kwladyka: i want prepare schema for data tapes and also required data

12:49 justin_smith {:id 1 :code "fsadfasd"} or {:id 1} or {:code "fdsfdsafads"}

12:49 _maddy: the problem is that I have a two-dimensional map, I need to add something to the inner maps (can't really understand how reduce-kv works)

12:49 kwladyka: perhaps i should use bouncer and schema together

12:50 justin_smith: (s/conditional :id {:id Number Keyword s/Any} :code {:code String Keyword s/Any})

12:50 _maddy: I know how normal reduce works

12:51 justin_smith: _maddy: reduce-kv is just like reduce, except it is optimized for hash-maps, and hands you the key and value as separate args

12:51 _maddy: maybe you can just use update-in or assoc-in ?

12:51 also, reduce is a lot more flexible than people take it for at first glance (as is reduce-kv)

12:52 _maddy: yeah, I usually think of reduce as reducing a collection to single value

12:52 justin_smith: _maddy: a hash-map is a single value :)

12:52 _maddy: sure

12:52 justin_smith: thanks to easy to use collections, a reduce can build up any structure you like

12:53 _maddy: but building up stuff feels contradictory to the name

12:53 justin_smith: yeah, I like the function, but the name (which is very historical) can be misleading

12:53 kwladyka: justin_smith the intention is not readable here but it should work

12:54 justin_smith: kwladyka: there could be a more idiomatic way to express it...

12:54 but the semantics are right at least

12:54 or should be close to it

12:54 kwladyka: i guess i will need some variants also, when X is present i also need Y and Z. Always complete. Not sure yet.

12:54 justin_smith: kwladyka: you could us s/conditional and then use functions that determine if a given variant is present

12:55 kwladyka: best option is stop using so many variant things!

12:55 kwladyka: not really sure what will be the end of requirements :) I am trying to write some schema for data like order, client, product, address to be consistent in whole system

12:56 justin_smith: kwladyka: one thing I like about regular schemas is that in the repl you can pprint them to remind yourself what the data here should look like

12:56 kwladyka: justin_smith i read about that, it should be helpful

12:57 anyway did you hear about some ready data schema for things like i describe?

12:57 maybe somebody did this work?

12:57 even in table in english which i can rewrite to clojure

12:57 it is hard work to design right schema

12:57 justin_smith: I have no idea... a library of common off the shelf schemas could be interesting

12:58 kwladyka: recently I did a massive refactor that would likely have been impossible without prismatic/schema - I captured the values the functions were getting from clients, then I wrote the strictest schemas I could that passed all those values, then wrote my new functions against those schemas

12:59 so much easier than trial and error when the data gets complex

12:59 and more accurate as documentation than any comments could be

13:00 kwladyka: schema looks cool, i think it will be very useful tool

13:01 especially for REST API

13:03 justin_smith: kwladyka: you might be interested in ring-swagger for this https://github.com/metosin/ring-swagger

13:03 kwladyka: justin_smith cool thx

13:08 jonathanj: so how do i fix reflection warnings for code that is something like: (defn [reason] (if (instance? Exception reason) a b))?

13:09 alex-weej: is there an os.path.join equivalent in clojure?

13:09 justin_smith: jonathanj: that's a tricky one

13:09 alex-weej: what is os.path.join

13:09 jonathanj: it joins several OS path components together with the OS-specific separator

13:10 alex-weej: and also deals with segments starting with a / so jumping back to the root

13:10 kwladyka: jonathanj http://clojure.wladyka.eu/posts-output/2015-08-24-how-to-improve-algorithm-speed.html#avoid_reflections

13:10 alex-weej: os.path.join(“foo”, “bar”, “/baz”) == “/baz”

13:10 assuming pathsep is /

13:11 _maddy: ok, I am not getting it, lets say I have map of maps: { "A" { :key 1 } }, how do I add new key/value to the inner maps? And by calculating that, I need the key of the inner map

13:11 kwladyka: jonathanj and there you have ready solution https://www.refheap.com/8bff42793e9abbeb9af803b23

13:12 to not throw an exception if you don't have class loaded

13:12 this one is hard one to solve :)

13:14 justin_smith: ,(java.nio.file.Paths/get "foo" (into-array ["bar" "baz" "quux"])) ; alex-weej this is ugly, but it works

13:14 clojurebot: #object[sun.nio.fs.UnixPath 0xfe5389 "foo/bar/baz/quux"]

13:15 justin_smith: if you call str on it you get the path as a string

13:15 alex-weej: https://docs.oracle.com/javase/7/docs/api/java/nio/file/Path.html

13:15 jonathanj: kwladyka: that's not what the code does

13:16 alex-weej: is there some project to wrap java APIs in more clojure-friendly terms btw?

13:16 kwladyka: jonathanj ?

13:16 jonathanj: kwladyka: the code does a if the object is an instance of an exception and b if it's not an instance of exception

13:16 alex-weej: would be cool to collect some of the more “obvious” bindings

13:16 justin_smith: alex-weej: there are N+1 projects

13:16 jonathanj: kwladyka: whether the class is loaded or not is not really important, unless i'm missing something

13:16 justin_smith: alex-weej: clojure is a java library, it's all accessible, and sometimes people want to add a friendly layer on top

13:16 kwladyka: jonathanj ok maybe i mentioned about that unnecessary

13:17 jonathanj did you look on http://clojure.wladyka.eu/posts-output/2015-08-24-how-to-improve-algorithm-speed.html#avoid_reflections ?

13:17 alex-weej: justin_smith: sure, i’m just wondering if i can pull-request a library of wrappers for things like java.nio.file

13:17 jonathanj: kwladyka: not yet

13:17 alex-weej: this doesn’t work anyway, user=> (str (java.nio.file.Paths/get "foo" (into-array ["bar" "baz" "/quux"])))

13:17 "foo/bar/baz/quux"

13:18 kwladyka: jonathanj it should explain how to solve your problem

13:19 shiranaihito: ,(str (java.nio.file.Paths/get "foo" (into-array ["bar" "baz" "/quux"])))

13:19 clojurebot: "foo/bar/baz/quux"

13:19 shiranaihito: what's wrong with that? :)

13:19 justin_smith: also, that's super easy to wrap in a vararg string function

13:20 bacon198`: alex-weej: a good number of projects are trying to making the java libraries more friendly

13:20 jonathanj: kwladyka: i don't actually understand how it solves my problem

13:20 bacon198`: which is great, because you get tried and tested libraries being wrapped nicely

13:20 jonathanj: kwladyka: the problem is that i cannot provide a type hint to a value that has a dynamic type

13:20 shiranaihito: bacon198` i sure hope they're not wrapping some goddamn commons-turdnuggets from 1998 or something

13:20 alex-weej: In [2]: os.path.join("foo", "bar", "/baz")

13:20 Out[2]: '/baz'

13:21 shiranaihito: ah.. Python :P

13:21 jonathanj: i think i'm going to have to change the calling code to convert Exception so that i don't need the test

13:21 shiranaihito: alex-weej what do you mean with that though? does python just ignore the first two elements? is that what you want?

13:22 TimMc: alex-weej: I believe that reducing over the segments with the File constructor does what you want, if no one has mentioned that...

13:22 justin_smith: TimMc: not with the example he gave

13:23 TimMc: Hmm, lemme find the code of mine that does this...

13:24 Ah, yeah, I see I ran into that problem as well.

13:25 https://gist.github.com/timmc/de9251eb5343bcc72ef20347ef24026c

13:25 Needs cleanup, since I wrote it quite a while ago. :-)

13:25 justin_smith: TimMc: yeah, line 6 should be 3 lines, but it's not super bad

13:26 kwladyka: jonathanj maybe i miss something but what you pasted here doesn't throw reflection wartning

13:26 jonathanj are you sure where is the source of this warning?

13:26 TimMc: justin_smith: I also don't need to capture f2, I can just use it and discard it in the conditional.

13:26 kwladyka: (defn foo [x] (instance? Exception foo)) is really free of this warning

13:27 i mean (defn foo [x] (instance? Exception x))

13:28 justin_smith: kwladyka: I was skeptical so I double checked, you're right

13:28 kwladyka: jonathanj so i pasted you my article to help you find the right place which makes this warnings

13:29 shiranaihito: alex-weej or you can join some strings and give them to File

13:29 ,(str (java.io.File. (clojure.string/join "/" ["foo" "bar" "baz"])))

13:29 clojurebot: "foo/bar/baz"

13:30 kwladyka: justin_smith i checked that too twice :)

13:30 justin_smith: shiranaihito: fails on alex-weej 's example though

13:30 alex-weej: doesn’t work when one of the segments is an absolute path e.g. my example

13:30 shiranaihito: ok

13:30 ,(str (java.io.File. "/foo" (clojure.string/join "/" ["bar" "baz"])))

13:30 clojurebot: "/foo/bar/baz"

13:31 justin_smith: that isn't any better

13:32 shiranaihito: well it does start with an absolute part :P

13:33 if one of the elements denotes an absolute path, it probably goes in first anyway

13:38 justin_smith: also, platforms are allowed to use separators other than "/"

13:40 TEttinger: J:☃Users☃Justin☃Code☃

13:41 shiranaihito: justin_smith and that means my little snippet was bad? :P

13:41 justin_smith: shiranaihito: it's a second way it doesn't address the initial question

13:42 which explicitly mentioned using the system's native separator

13:42 shiranaihito: chill :)

13:42 kwladyka: shiranaihito i believe i saw some ready solution for that in the past.... maybe

13:43 shiranaihito: ,(str (java.io.File. (str java.io.File.separator "foo") (clojure.string/join java.io.File.separator ["bar" "baz"])))

13:43 kwladyka: but it can be my imagination

13:43 clojurebot: #error {\n :cause "java.io.File.separator"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.ClassNotFoundException: java.io.File.separator, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyze "Compiler.java" 6688]}\n {:type java.lang.ClassNotFoundException\n :message "java.io.File.separator"\n :at [java.net.URLClassLoader$1 run "URLClassLoade...

13:43 shiranaihito: alright.. something like that anyway :P

13:43 kwladyka solution for what? the file path stuff?

13:43 justin_smith: ,java.io.File/separator

13:43 clojurebot: "/"

13:43 shiranaihito: yeah, just realized

13:44 kwladyka: shiranaihito some library to do that or Java function.... i don't really remember. Some solution to make the right path

13:44 shiranaihito: yeah, it's not that complicated

13:44 i figured joining strings and then just handing them to File would be "neater"

13:44 or simpler (etc whatevs) than reducing with Files

13:45 kwladyka: i believe anyway you miss many not obviously things, like for example different systems

13:45 path in the windows for example can looks like c:/foo/bar

13:45 shiranaihito: sure, but that's what further development is for :p

13:45 we're just swapping snippets to help people

13:50 _maddy: my god, after all this work I have a data structure which looks really cumbersome..

13:54 alex-weej: is there some popular or built in library for running subprocesses and dealing with their events (e.g. stdin/stderr output, process exit) via an event loop or something?

13:56 shiranaihito: alex-weej: Java *kind of* has support for that

13:56 look at ProcessBuilder for example

13:56 justin_smith: there's ProcessBuilder and also conch which wraps ProcessBuilder / Process

13:56 shiranaihito: but for whatever it's worth, i couldn't get communicating with subprocesses to work when i tried

13:57 justin_smith: shiranaihito: each Process object has two inputstreams and an outputstream

13:57 shiranaihito: yeah

13:57 as far as i know, i was using it right.. not 100% sure ofc

13:58 justin_smith: I've done it before, it worked fine

13:58 shiranaihito: but something was wrong when trying to do input/output with processes

13:59 alex-weej: conch looks interesting thanks - wondering if i should try to shoehorn something into core.async

14:00 i want to have a bunch of subprocesses running at any one time and just select on their events

14:04 justin_smith: alex-weej: you could have regular threads that glock on read from the inputStream of the processes

14:05 alex-weej: don't do that in go blocks, go blocks are not for blocking operations

14:05 alex-weej: one per process?

14:05 justin_smith: yeah, and a single go block reading the results they put on the channel

14:05 alex-weej: sounds simple enough - is it actually reasonable straightforward to start threads in clojure?

14:06 kwladyka: alex-weej what exactly do you want to achieve? maybe you want just share state?

14:06 alex-weej: i just want to run a set of subprocesses and then multiplex their events in a loop

14:07 events are like - ready for read on stderr, ready for read on stdout, ready for write on stdin, process terminated with exit code N

14:08 justin_smith: alex-weej: shiranaihito: simple repl example of i/o with a process in the repl (in a real example you would read to a byte-array, not read a byte at a time, of course)

14:08 https://gist.github.com/noisesmith/f2ca73a0a6b0e5d686487c6084196a97

14:08 alex-weej: if this was low level linux i would fork and exec and do some fd sharing, and just epoll the fds without the use of any threads

14:08 justin_smith: alex-weej: the api doesn'

14:08 t expose "ready for" anything, sadly

14:09 which is why I mentioned threads doing blocking reads/writes

14:10 shiranaihito: justin_smith thanks :) i think my main problem was taking stuff from one process and handing it into another, but i just scrapped what i was doing and went with Python instead

14:10 .. and Python did exactly what i wanted with like a couple of lines of code :)

14:11 justin_smith: shiranaihito: the biggest gotcha is needing to do the .flush method iirc

14:11 shiranaihito: i'm pretty sure i flushed stuff too.. i've used Java for a long time

14:12 justin_smith: the problem with system stuff in java is it is committed to the lowest common denominator, and there are some really low targets it had to hit in the past (Win9x etc.)

14:12 shiranaihito: oh, OK

14:12 shiranaihito: I built a crazy system that piped the contents of a 16 channel sound card to 16 separate programs in clojure - which was amazing and also a pain in the ass

14:12 but it worked!

14:12 shiranaihito: basically i was trying to: start a process, feed it input, wait for it to complete (working on the input), and then hoping to take out whatever it produced.. and hand it off to another process

14:13 justin_smith: shiranaihito: see, the people who wrote the software spec assumed there was a "read one channel of a sound card" api - which of course doesn't exist anywhere

14:13 shiranaihito: wow, sounds hard-core

14:13 justin_smith: I didn't really grok core.async until I completed that project :)

14:13 shiranaihito: :)

14:13 i haven't even tried using it yet

14:14 jonathanj: can i add a type hint to a parameter inline?

14:14 `(.someMethod obj ^long value)` for example

14:15 justin_smith: alex-weej: a lot of the stuff you are trying to do is easier in pixie, which is still kind of experimental, but it's a clojure implemented using rpython's compiler (but there is no python available at runtime mind you)

14:15 shiranaihito: "inline"?

14:15 justin_smith: jonathanj: yeah, that should work

14:15 kwladyka: jonathanj why not in the definition of function?

14:16 i mean in the parameters

14:16 jonathanj: kwladyka: because it's just a minor intermediate result

14:18 kwladyka: jonathanj i guess more readable it will be when you use ^long somewhere when this result comes, i don't know what you use, let or something

14:22 _maddy: how do I configure cider repl so that I have to use ns in front of functions (I want to have functions of same name in 2 files)

14:23 kwladyka: _maddy just use namespaces when call this functions

14:24 foo/f and bar/f

14:24 hodwik: If I want to send a header param as part of a post with clojure-http, do I just make a map {:headers {:API_KEY

14:24 "APIKEY"}}

14:24 and send that as part of the post?

14:28 alex-weej: is there a common idiom or builtin for combining let with if? e.g. (let-if [x y] then else) -> (let [x y] (if x then else)) - to avoid repeating x

14:29 ridcully_: hodwik: that's how it works in clj-http. did you mean this or the clojure-http-client lib?

14:30 hodwik: @ridcully sorry, that's what I meant

14:33 jonathanj: the type hints from protocols are disregarded in defrecord implementations?

14:33 kwladyka: alex-weej if-let still you don't try use cheatsheet, just click there ctrl+f in your browser

14:34 hiredman: they don't exist

14:35 e.g. you can attach metadata to the parameters in a protocol, but it doesn't do anything

14:35 alex-weej: kwladyka: i see it’s spelled if-let, that makes more sense actually!

14:35 thanks

14:36 hodwik: Could anyone with clj-http experience tell me why this: http://pastebin.com/8nwSeqVA is spitting out:Exception in thread "main" java.lang.ClassCastException: clojure.lang.PersistentArrayMap cannot be cast to [B, compiling:(stfr_bot/core.clj:74:1)

14:36 the variables are all def'd before that section

14:38 ridcully_: hodwik: i think it has to be :json there. also i doubt it will deal with the json conversion anyway

14:40 hodwik: something like this: https://github.com/christoph-frick/rest-repl/blob/master/src/rest/public.clj#L56-L59

14:41 TEttinger: [B is that wacky stacktrace-speak for byte array

14:45 justin_smith: ,(class (.getBytes "foo"))

14:45 clojurebot: [B

14:46 hodwik: ridcully: Awesome! Thanks so much. I was trying to use Cheshire to encode my json, but just doing it with clj-http was much easier

14:49 _maddy: how do I pass-through variable arguments to another function? I can create optional args with &, but they are set to nil (and sent as nil to the next func, which is a problem)

14:49 justin_smith: _maddy: if you use arity overloading instead of &, then it's much easier to provide defaults

14:50 ,((fn foo ([] (foo

14:50 clojurebot: #<RuntimeException java.lang.RuntimeException: EOF while reading>

14:50 justin_smith: err

14:50 ,((fn foo ([] (foo "hi")) ([message] message)))

14:50 clojurebot: "hi"

14:51 _maddy: I could use arity overloading, but it looks like then I have to repeat the function body multiple times

14:51 which is not nice

14:51 justin_smith: _maddy: no, that's never needed

14:51 _maddy: look at my example, no repitition

14:52 ,((fn f ([] (f 2)) ([x] (* x x x)))

14:52 clojurebot: #<RuntimeException java.lang.RuntimeException: EOF while reading>

14:52 _maddy: oh, but the problem is that I don't want to define defaults here, they are defined in the inner function which I am calling

14:52 justin_smith: ,((fn f ([] (f 2)) ([x] (* x x x))))

14:52 clojurebot: 8

14:52 _maddy: where I already have arity overloading pattern

14:53 I think I am too tired, have to continue on another day :)

14:53 really liking clojure now that I am slowly starting to understand it

16:05 beaky: i love clojure too

16:23 jonathanj: if i have a protocol method defined like (foo [this ^SomeObj x]) and i have the implementation (on defrecord, for example) like (foo [this ^SomeObj x]) then i get a compile error like: Can't find matching method: foo, leave off hints for auto match.

16:24 if i omit the type hint for the implementation then it compiles but `x` is not type hinted

16:29 TimMc: Could it be that you need to throw more type hints at it? I haven't worked with protocols recently, but I wonder if "this" or the return value need hinting (seems like overkill though).

16:33 amalloy: protocol functions don't support type hinting anyway

16:44 jonathanj: how do i unambiguously import two java classes with the same name?

16:44 clojurebot: excusez-moi

16:47 amalloy: the same way you do it in java: only import one of them, and refer to the other fully-qualified

16:49 jonathanj: i don't recall ever having to do this in java, so i wasn't aware if there was another way

16:49 TimMc: There's another option, which is .importClass...

16:49 jonathanj: C# has some way to alias stuff, iinm

16:51 TimMc: If you don't mind potentially messing up some automated tooling and IDEs, you can do (.importClass *ns* 'RLN foo.bar.ReallyLongName)

16:51 stick that right after your ns block and say two Hail Marys

16:52 jonathanj: haha, i'll just go with the fully qualified option

16:57 TimMc: I've done this once and never had a problem with it, just... you know, fair warning.

17:02 jonathanj: like that guy who puts his hand into the crocodile's mouth and doesn't get bitten, then he calls his friends over to see his neat trik

17:08 TimMc: jonathanj: There are two risks: 1) It might go away (binary .importClass is not used anywhere I can see in 1.7.0), and 2) tools might expect all imports and requires to be parseable from the ns block.

17:08 So: Possibly annoying, but it's not like the program will behave incorrectly if it runs at all.

17:24 fikgol: why storm used maven instead of leiningen after 0.9 ?

17:25 TEttinger: fikgol: I might be thinking of a different storm

17:34 fikgol, ah https://github.com/apache/storm/pull/14

17:34 apparently their build process is complex

18:03 vermiculus: i've got an algorithm challenge if anyone fancies a crack at it

18:03 it's a recursive question involving maps

18:06 TEttinger: me crack

18:07 vermiculus: TEttinger: :)

18:07 Say you have a structure like {1 {:parent :root, :slug "lovely"}, 2 {:parent 1, :slug "lovelier"}, 3 {:parent 1, :slug "loveliest"}, 4 {:parent :root, :slug "cool"}}

18:08 Notice that this is pretty much a tree of slugs

18:08 I'd like to get the structure {1 "/lovely" 2 "/lovely/lovelier" 3 "/lovely/loveliest" 4 "/cool"}

18:08 justin_smith: oh, nice, a graph

18:09 vermiculus: :-)

18:10 justin_smith: vermiculus: what would you want if something had :parent 3?

18:10 what if 5 had :parent 6 and 6 had :parent 5? what would print then?

18:10 vermiculus: justin_smith: I'd say behavior is undefined in those cases.

18:10 Probably throw some sort of error.

18:11 justin_smith: so you have a representation that can do arbitrary graphs, but behavior is only defined for tree (directed and acyclic)

18:11 vermiculus: justin_smith: correct

18:11 turning the starting structure into a tree is, of course, a valid approach

18:16 TEttinger: ,(let [structure {1 {:parent :root, :slug "lovely"}, 2 {:parent 1, :slug "lovelier"}, 3 {:parent 1, :slug "loveliest"}, 4 {:parent :root, :slug "cool"}}] (into {} (map (fn [[k {:keys [parent slug]}]] [k (str (get-in structure [parent :slug] "") "/" slug)]) structure))) ; not yet ready

18:16 clojurebot: {1 "/lovely", 2 "lovely/lovelier", 3 "lovely/loveliest", 4 "/cool"}

18:17 TEttinger: this only handles one level, which is the first problem

18:17 the lesser problem is not getting the opening / right

18:18 justin_smith: (get-in structure [parent :slug] "/")

18:18 shoud work!

18:19 oh, wait

18:19 never mind, your thing is good, you just need to make it recursive

18:34 vermiculus: I have a solution now, but it's pretty ugly :)

18:35 It's also a little cheat-y IMHO

18:35 https://gist.github.com/vermiculus/c8a531a86a0a65cdb9af4a466ed0721f

18:38 ,(defn get-topic-children [topics topic-id] (map first (filter #(= topic-id (:parent (second %))) topics)))

18:38 clojurebot: #'sandbox/get-topic-children

18:39 vermiculus: ,(defn get-routes [topics topic-id & [slug-so-far]] (let [children (get-topic-children topics topic-id) newslug (if (= topic-id :root) "" (str slug-so-far "/" (:slug (get topics topic-id))))] (if children (into (if (= topic-id :root) {} {topic-id newslug}) (map (fn [child-id] (if child-id (get-routes topics child-id newslug))) children)) {topic-id newslug})))

18:39 clojurebot: #'sandbox/get-routes

18:39 vermiculus: ,(get-routes {1 {:parent :root, :slug "lovely"}, 2 {:parent 1, :slug "lovelier"}, 3 {:parent 1, :slug "loveliest"}, 4 {:parent :root, :slug "cool"}} :root)

18:39 clojurebot: {1 "/lovely", 2 "/lovely/lovelier", 3 "/lovely/loveliest", 4 "/cool"}

19:03 vermiculus: TEttinger: Feel free to ping me on twitter if you find a cleaner solution :-) (same username: @vermiculus)

19:03 * vermiculus finds some dinner

20:02 asdf12z_: (defroutes app (GET "/ws" request (ws-handler request))) can someone explain to me how does "request" work here? this is compojure

20:02 how is request even defined?

20:02 like how is it possible to pass `GET` the result of (ws-handler request) when request isn't defined yet?

20:03 i'm guessing its part of the macro magic

20:03 justin_smith: asdf12z_: defroutes is a macro, and it's binding the client's request object to the symbol 'request

20:03 asdf12z_: in normal clojure macros, request would be in a vector (eg let or defn)

20:04 I guess they decided compojure does not need a vector, because your handler is guaranteed to receive exactly one argument? not sure

20:05 asdf12z_: i guess i'm just confused as to how the code is evaluated and executed, so everything in defroutes doesn't actually eval yet, it's all fed in, and the macro then fills in for all the argument stuff like request

20:05 is that about right?

20:05 justin_smith: well macros are allowed to do anything they want to their input - take it as is, evaluate it in the lexical environment, whatever

20:06 macroexpand might be informative here

20:08 asdf12z_: ok thanks justin_smith

Logging service provided by n01se.net