#clojure log - Mar 21 2015

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

0:45 arav93: Goodnight! :)

2:48 aytch: #16 is a bit tricky

2:49 (= (__ "Dave") "Hello, Dave!")

2:49 the naive response (mine) was: str "Hello, "

2:50 but that doesn't evaluate as true, because it needs the "!" at the end

2:52 (= (str "Hello, " "Dave") "Hello, Dave")

2:52 true

2:52 (= (str "Hello, " "Dave") "Hello, Dave!")

2:52 false

2:52 TEttinger: yep

2:52 so you need to define a function there

2:53 aytch: yeah, I'm realizing that

2:53 it's just an exponential leap in difficulty

2:53 strings were only introduced in a question or two before

2:53 Seylerius: What's this: koans or something?

2:53 Or 4clojure?

2:53 aytch: Seylerius: 4clojure

2:54 TEttinger: (fn [name] (str "beginning " name " end"))

2:54 aytch: the title of the problem is "Hello World"

2:54 not "String Concatenation"

2:54 TEttinger: heh, hello world usually involves string concatenation as the second step

2:54 hello yourname

2:55 aytch: Absolutely, I agree

2:55 TEttinger: 4clojure definitely gets challenging quickly

2:55 have you reimplemented nth yet?

2:55 Seylerius: Annoyingly, 4clojure doesn't let me sign up.

2:56 aytch: I'm guessing no

2:56 TEttinger: that's one of the tasks that comes up around 20-30-something I think

2:56 it's a good challenge

2:57 amalloy: Seylerius: doesn't let you sign up?

2:58 Seylerius: Well, it let me sign up, but it keeps giving me an error on log in, and not a password error.

2:58 Okay, the problem seems to have been related to my username being capitalized.

3:26 aytch: I don't know who _pcl is, but all their answers match up with mine. And other people do some weird voodoo.

4:13 Seylerius: Random but funny bit of dialogue from something I read recently: "Great. That probably means I'll be shooting eyebeams by next week." ;; "That was actually a party trick of your great-grandfather's, if I remember the stories correctly. He used it to incinerate enemies, destroy citadels and open canned goods, for which I am sure that your great grandmother was ever grateful."

4:13 Totally off topic, but amusing.

4:42 Y'know what's weird? Sometimes the 4clojure tests display one of the tests as failed, even though the solution is correct and the problem gets marked as solved.

4:42 Only seems to happen on the multi-test problems.

4:50 aytch: Seylerius: on the multi-test problems, there is only one correct solution.

4:51 the code will either pass all of the tests, or none of them, but the graphics are misleading.

4:51 Seylerius: aytch: Exactly. And I submit the right one, the system marks that I've passed, but one of the tests gets a nice red dot, even though all logic says it should have passed that test too.

4:52 It's very odd.

4:52 aytch: errr....there are multiple correct ways to implement the solution, but one solution must pass all tests.

4:52 I was asking about this a few days ago.

4:52 Seylerius: There must be some kind of bug in the rendering.

4:53 It can't really be failing that test, because I wouldn't get a "Solved!" result, but it still looks like it failed that test.

4:53 Very strange.

4:53 aytch: Which #?

4:53 Seylerius: I can't remember which one did it last. Maybe 17 or 18? I'll pipe up next time it does it.

4:54 19 is... interesting.

4:54 Reimplement last.

5:45 http://sprunge.us/KYef

5:46 That's failing 4clojure #21: Nth Element

5:46 Oh, wait...

5:46 Nevermind

6:28 anti-freeze: Hey guys/girls. Does anyone have any issues with mongodb timing out?

6:29 deanm: Hello, can you please suggest an easy reading introduction to Clojure? I've tried a couple like braveclojure.com and "The Joy of Clojure" book but find it they move too fast for my understanding

6:33 anti-freeze: deanm: Maybe just clojure in action or programming clojure. I hear those are good. There's a new one soon to be released by o'reilly "Living clojure"

6:40 deanm: Thanks anti-freeze, ill have a look at them as well

6:46 anti-freeze: Sorry, my mac went to sleep. Did I miss anything?

6:48 deanm: anti-freeze: nope, other than me saying thanks for the tips

6:48 anti-freeze: deanm: Ah, now that I remember. Probably the best intro is clojure for the brave and true

6:48 deanm: Its free online

6:49 deanm: anti-freeze: Yeah i tried that but at some point around destructuring i couldn't follow up and had to look up other references on the net so it occured to me that maybe im too dumb for that and need something more easy

6:50 anti-freeze: deanm: The thing with clojure is that it needs perseverance. Its frustrating at first

6:50 deanm: It was easier for me coming from a ruby background, where it was sort of pseudo functional

6:51 deanm: anti-freeze: Ok nice to hear that, i come from a Java background although i don't program these days anymore

6:52 anti-freeze: deanm: That's probably why its frustrating. Check this out: http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html

6:54 Anyone with suggestions on the mongodb issue?

6:54 T'was working yesterday, I swear it

7:12 Also, how the hell do I access bounded variables from a macro. Say I have a macro that defines found-user from a DB query. I'm currently using found-user# gensyms, but I wan't access it. For example (if-valid-user params (login-success found-user))

7:21 Glenjamin: ,`~'symbol

7:22 clojurebot: symbol

7:22 Glenjamin: anti-freeze: you can use ~'symbol inside the syntax-quote to get a bare symbole

7:23 although in general it might be worth asking yourself if the macro provides any benefit over a function

7:24 anti-freeze: Glenjamin: Something like this? https://ideone.com/DjLZHZ I asked myself that, but it seems to provide more clarity

7:26 Glenjamin: (if-let [user (user-logged-in? params)] ...)

7:26 you should be able to make that work with roughly the same code and no macro

7:26 Bronsa: anti-freeze: it's usually better to use gensyms rather than doing the ~' trick to get unqualified symbols

7:26 Glenjamin: maybe that should be called (logged-in-user params)

7:27 anti-freeze: Bronsa: I know, but i still want to access the found-user value to add his ID to session

7:28 Bronsa: anti-freeze: also you're trying to get runtime values at constant time

7:28 compile time*

7:28 anti-freeze: Bronsa: Well shit, how would you do it?

7:28 Glenjamin: the macro pasted is just some sugar for an if statement

7:28 anti-freeze: Ah, I give up...

7:29 Bronsa: anti-freeze: I'm talking about that cred/pass let. you don't seem to actually use those locals though

7:29 anti-freeze: Mongo doesn't work, I can't get simple authentication working...

7:31 Bronsa: anti-freeze: it's usually a good idea to have a solid understanding of how the language works before trying to write your own macros

7:31 anti-freeze: Bronsa: Yup, makes sense. I was swayed to clojure by all the preaching and what not of how amazing it is

7:32 Glenjamin: macros are a funny one, they're a headline language feature, but actually not used particularly often

7:32 i overused macros quite a bit in my first clojure project

7:33 anti-freeze: How do I pass the then else branches then?

7:33 Glenjamin: just use a normal if form

7:33 anti-freeze: Glenjamin: Ah, makes sense

7:33 makes cents?

7:36 Glenjamin: sense is correct

7:36 something like this: https://www.refheap.com/98698

7:37 anti-freeze: Glenjamin: Yea, that's what I changed it to. Hold on, let me do some copypasta

7:38 Glenjamin: https://www.refheap.com/7c95d4846cc402dccbf87c6c5

7:38 Glenjamin: yep, you can shrink it down a bit with some destructuring and more if-let, but thats the right idea

7:41 anti-freeze: Glenjamin: Alright man, thanks a bunch

10:12 Hi again everyone. I can't seem to access the return values of mongodb insert operations. I just want to get the insert ID. Any ideas?

10:16 Simply, is there a way to convert a monger WriteResult to a map?

10:19 gfredericks: anti-freeze: is a WriteResult a defrecord?

10:22 anti-freeze: gfredericks: Its a java class I think. I can't just reference properties with (:_id rec)

10:22 gfredericks: but monger is a clojure library?

10:23 have you tried clojure.core/bean?

10:25 anti-freeze: gfredericks: Just did, got me nowhere

10:31 justin_smith: anti-freeze: unrelated question - what exactly is the :flash key there?

10:31 anti-freeze: justin_smith: Flash key? There is none. I'm running tests and as part up setup I want to store a newly inserted users ID to check against the user ID stored in session

10:32 justin_smith: anti-freeze: I mean the :flash key on line 16 of https://www.refheap.com/7c95d4846cc402dccbf87c6c5

10:33 anti-freeze: justin_smith: a map containing errors and the previously passed parameters for dispatch to the view

10:33 justin_smith: anti-freeze: is putting stuff like that under a :flash key an idiomatic thing? is this related to the wrap-flash ring middleware?

10:34 anti-freeze: justin_smith: I don't know. I believe it is, yea

10:42 Don't worry about the mongo thing guys, you're supposed to use insert-and-return. I don't know why that's not default behaviour, but ok

10:57 gfredericks: maybe insert is async?

10:58 I don't know nuthin bout mongo so I just assume it defaults to doing the fast-uncertain thing

10:58 justin_smith: gfredericks: yeah, I think so. Because nosql.

10:59 anti-freeze: I'm not sure. I'm just using it for the schemaless stuff

10:59 One more question... for now. How would you test macros?

11:00 Something like this: https://www.refheap.com/98704

11:01 gfredericks: write some tests that use them maybe

11:01 is the whole point of these to be anaphoric?

11:02 I don't think testing macros is too crucial unless they're really complicated

11:02 anti-freeze: gfredericks: Not really, its just that I was always writing (POST "/route" request (func request))

11:02 gfredericks: just test the code that uses them

11:02 justin_smith: anti-freeze: what's up with ~'request ?

11:03 oh

11:03 anti-freeze: justin_smith: I want to use the symbol request. So the result is (REQUEST-POST "/route" func) => (POST "/route" request (func request))

11:04 justin_smith: right, because you are generating a macro call with your macro

11:04 anti-freeze: justin_smith: Pretty much. I can't pass macros around unfortunately, or else I would have a REQUEST-* macro, which has the type of the request passed to it

11:05 gfredericks: I guess the macros work just fine then

11:38 Is anyone planning to implement an interactive clojure debugger by the way?

11:43 Igor: I run "lein uberjar" but it print "compiling" and that's all, jar doesn't appear

12:01 not-much-io: Hi all, I am trying to complete a HackerRank challenge with Clojure, but unfortunately my code is not cutting it speed wise. Using the time macro also didn't really show any bottlenecks.. Any suggestions? -> https://gist.github.com/KristoKoert/17ad1960ec2058388e63

12:02 Glenjamin: you can try using a profiler like Java VisualVM

12:03 not-much-io: I suppose it's as good a time as any to learn to use a profiler :)

12:20 Igor: I found solution - every code must be in functions

12:21 not-much-io: What do you mean?

12:23 It's purely a performance issue as I see it because about 5 test cases pass, however as the inputs get larger timeouts occur because the program won't finish under the 8s cap.

12:24 Igor: I try get uberjar, but it doesn't work until I delete code outside funcs declarations

12:25 not-much-io: Well yes because input-list isn't defined

12:26 I can't really trivially upload a workable uberjarrable example because the input is approx a miljon characters long.

12:26 Glenjamin: you're both talking about different problems.

12:27 justin_smith: isn't hackerrank the one where clojure always performs poorly because they include the clojure bootstrapping time in the perf timing?

12:28 not-much-io: Scala and Clojure are given extra time for that as I understand it.

12:28 justin_smith: oh, OK

12:28 not-much-io: But I saw that others had done the challenge in Clojure

12:29 justin_smith: it's not about jvm overhead though. It's about the clojure compiler bootstrap

12:29 OK

12:30 not-much-io: I think your keyword conversions are not needed

12:30 not-much-io: It's only a 10 point excercise so I was surprised my solution didn't pass the time constraints. Thought I had done something obviously stupid :)

12:30 justin_smith: just use the string as the key in the hash

12:31 not-much-io: also, yeah, every top level form should be a def/defn or some other similar declaration, not side effects and not regular code

12:31 for uberjar reasons, among other reasons

12:31 eg. loading a namespace should not have side effects

12:32 not-much-io: Yes I am aware of that, the outer expression was just to feed the results into the HackerRank engine.

12:32 Maybe I should not have included it, my bad.

12:32 Checking on the keyword conversion thing..

12:32 justin_smith: ahh, right, I was referencing your prior comment "I found solution - every code must be in functions"

12:33 not-much-io: only use keywords for literals in code (or what you expect the end user to provide as literals)

12:33 not-much-io: No, that wasn't me :D

12:33 justin_smith: if it's a string, it can stay a string

12:33 not-much-io: oops, my bad :)

12:34 not-much-io: Okey, but is it really that expensive of a converion? :O

12:34 I did it more for style than anything, but ofcourse it was pointless in this case

12:35 justin_smith: not-much-io: well I'd think it's a minus for style unless somebody is using the keywords as literals (which doesn't appear to be the case)

12:35 and yeah, repeated keyword conversions aren't super expensive, but they are not free either

12:36 not-much-io: Agreed that it's a minus

12:37 justin_smith: oh wow, I found a bug in criterium

12:37 lol

12:38 not-much-io: A bug?

12:38 justin_smith: Execution time mean : -3.551208 ns

12:38 I don't think there's such thing as code with a negative runtime

12:38 not-much-io: BTW there was a a conversion from char -> str -> keyword going on

12:39 justin_smith: oh wow, yeah, just use the char!

12:40 check out this silly benchmark https://www.refheap.com/98711

12:44 not-much-io: Unfortunately removing the conversions had no effect :(

12:44 justin_smith: have you tried profiling? jvisualvm is pretty accessible

12:44 sometimes just called visualvm

12:44 not-much-io: I was in the process of learning to use it, haven't used it before.

12:45 justin_smith: you should be able to find the cpu profiling tab

12:45 the issue after that is that it will show you stuff in terms of the jvm impl, not the clojure abstractions

12:46 not-much-io: Do I need to generate a uberjar to use it? I usually just use the repl for these kind of problems.

12:46 justin_smith: no, visualvm can connect to your repl

12:46 turn on cpu profiling, then run your main function

12:46 or whatever other function you want to profile in detail

12:47 you should get a clickable for each running jvm on the left side of the ui

12:47 not-much-io: Oooooh, thats all?

12:47 That was easy

12:47 justin_smith: yeah, it's easy, point and click

12:47 heh :)

12:47 not-much-io: Thanks! I would probabl have wasted a lot of time trying to use it :D

12:47 getMethods() are the mainusage

12:48 justin_smith: reflection!

12:48 try turning on reflection warnings vie project.clj

12:48 and fix the warnings

12:49 not-much-io: But wont that then show me something different than what runs on HackeRank?

12:49 justin_smith: the warnings tell you what you need to fix

12:49 once it's fixed, it's fixed regardless of where it runs

12:49 not-much-io: Do you mean the leinigen project.clj? Because I don

12:49 justin_smith: you use hints so that clojure doesn't need to use reflection to figure out which method to run

12:49 not-much-io: 't even have a separate project

12:50 justin_smith: in that case, start up the repl with reflection turned on

12:50 just make sure *warn-on-reflection* is true, whatever method works for your repl

12:50 it's just convenient to do that from project.clj

12:51 not-much-io: Can I change it in a runnig repl?

12:51 justin_smith: I'm trying to think of how it's done, I haven't done it recently...

12:52 (set! *warn-on-reflection* true)

12:52 that will work for the current thread, and any agents / futures created from that thread

12:52 if you use Thread though, it might not propagate

12:53 not-much-io: Nope, no threading currently

12:53 okay, much less gets now

12:54 justin_smith: then run your code, and where you get reflection warnings, add hints about the actual type that will be seen at runtime

12:55 not-much-io: 1. reflector.getMethods() 2. rule3.invoke() 3. PersistenArrayMap.assoc() 4. rule4.invoke()

12:56 Reflection still on maybe..

12:56 justin_smith: not-much-io: if you have reflection warnings turned on, then defining your functions should make the clojure compiler tell you where the reflection is happening

12:57 *warn-on-reflection* is about giving you guidance to where type hints are needed to speed up your code

12:57 it doesn't actually make anything faster by itself

12:57 not-much-io: So you are suggesting I add type hints?

12:58 justin_smith: not-much-io: that's how you make clojure not spend time in reflector.getMethods(), yeah

12:58 and that will often be the biggest perf gain you can get in a clojure program

12:58 not-much-io: Okey, now I understand.

12:58 Makes sense.

12:58 Thanks, learned alot. :)

12:58 justin_smith: this is especially true for math

12:59 where it can compile direct arithmetic ops for the correct type, instead of a bunch of reflection and generic math methods

12:59 not-much-io: Still I am surprised something simple like this requires type hints. EIther my algorithms sucks or the HackerRank people didn't bother with trying clojure. :)

13:00 justin_smith: not-much-io: if you want good performance, clojure requires type hints

13:00 not-much-io: Okey, so to reiterate: If the profiler shows me a lot of time spent in Reflector.getMethods() it's a reflection thing?

13:00 justin_smith: yes

13:01 and you can fix your code so that Reflector.getMethods() never gets called

13:01 J_Arcane: not-much-io: I'm finding that some of the online exercises for Clojure are a bit rubbish. Codewars' clj support is a mess.

13:01 justin_smith: reflection is very expensive

13:01 not-much-io: I though as much about the type hints, it's just it hasn't been a problem until now, even with much harder problems.

13:02 justin_smith: or, to be more explicit - code that requires reflection is very hard for the vm to optimize, and the reflection process itself is slow too

13:04 not-much-io: another thing that is going to add up over 100000 calls is the usage of a persistent data structure. If the code path is such that it wouldn't need copying, a simple four element array is going to speed things up a lot since so much of your code is just getting things from / updating values in that four element data structure

13:05 in general persistent data structures are awesome, but sometimes it can be safe to use a mutable thing, and it can be worth it for the 10x or more perf gain

13:05 that would also make your code uglier though

13:06 not-much-io: God damn it, sutpid question, how do you refer to someone in IRC :D

13:06 justin_smith: most clients will highlight usages of a nick

13:06 and will tab-complete a nick too

13:06 not-much-io: justin_smith Test

13:06 justin_smith: right, that highlights in my client

13:07 not-much-io: Okey

13:07 J_Arcane Probably the pains of a growing language :)

13:08 justin_smith: not-much-io: also the pains of a language that requires the full compiler to be loaded before code can be run

13:08 not-much-io: justin_smith Thanks for the tips, I'll certainly keep them in mind.

13:09 justin_smith: np - if you can eliminate reflection, and use a simple array in a tight loop when it's safe to do so, there's nothing keeping clojure from being at least as fast as java

13:09 J_Arcane: Argh. Could you repeat that? I missed it because my client crashed ...

13:10 not-much-io: justin_smith J_Arcane It's probably the pains of a growing language and justin_smith added that it's also a problem of a language that requires loading the full compiler.

13:10 Is reflection the main culprid for runtime speed?

13:10 J_Arcane: Yeah. The Clojure world in general does have a very 'beta' feel at times. :)

13:11 justin_smith: not-much-io: yeah, reflection followed by persistent data structure overhead. Never any reason not to eliminate the former, the latter is worth it for correctness unless you need that last 8-10x speed increase.

13:12 not-much-io: I wonder if HackerRank took Clojures startup time into account? I doubt it, iflooking at Clojure vs. Scala -> https://www.hackerrank.com/environment

13:13 justin_smith Awesome, that is worth knowing. I sort of did, but never really approached the problem :)

13:14 justin_smith: not-much-io: and typically your profiling will give you a good lead - whether you are spending a lot of time in reflection, in generic math ops, in persistent data structure allocations...

13:15 Bronsa: I kinda wish one had to allow the compiler to use reflection explicitly

13:15 justin_smith: Bronsa: oh, that's a great idea for a feature

13:15 (set! *warn-on-reflection* :pedantic)

13:15 haha

13:16 not-much-io: As I've looked around, it seems a lot of peole are discontent that clojure is dynamically typed..

13:16 hyPiRion: I'd like the compiler to detect uniqueness and do transient magic on the persistent data structures.

13:16 Bronsa: justin_smith: wouldn't make much sense to add that but not make it the default

13:16 hyPiRion: But the persistent data structures are actually very good performance wise, really.

13:16 justin_smith: Bronsa: I don't think it would fly unless it was opt-in though

13:17 Bronsa: justin_smith: right, it's too late to change that in clojure unfortunately, I guess

13:17 justin_smith: hyPiRion: it's true, but I've also seen the difference a switch to an array in a tight loop can make (because of the mixture of eliminating allocation + cache coherence)

13:18 for primitive types that is

13:18 Bronsa: btw clojure doesn't really need the whole compiler to run code -- when it AOTs it just needs the runtime

13:18 justin_smith: Bronsa: but being able to turn it on for my own code would be great

13:18 Bronsa: but will it actually run without loading the compiler from AOT ?

13:18 hyPiRion: justin_smith: yeah, true enough.

13:18 Bronsa: justin_smith: would it help you more than (set! *warn-on-reflection* true)?

13:18 justin_smith: Bronsa: I thought the compilation stuff would be loaded regardless

13:19 Bronsa: maybe not

13:19 Bronsa: also, in the context of the code challenge there, I don't think they let you submit an uberjar? not sure though.

13:20 Bronsa: justin_smith: what i mean is, once you AOT compile clojure.lang.Compiler is not necessary anymore as there's no clj compilation involved while loading

13:20 not-much-io: justin_smith Not in HackerRank no

13:21 justin_smith: Bronsa: OK. So unless your own code causes it to be loaded at runtime, it won't be.

13:21 I guess I should have ended that with a question mark, as it was a question :)

13:39 not-much-io: justin_smith: Reporting back, with typ hints to Math/abs performance increase of ~8 times :)

13:45 gfredericks: ~reflection

13:45 clojurebot: Excuse me?

13:47 chenglou: why doesn't this terminate? (def a (range)) (def b (rest a)) (def c (rest a)) (= b c)

13:47 not-much-io: (range) is a infinite sequence

13:48 chenglou: yes, but I thought b and c were just pointers to the same "next" slot in a

13:48 not-much-io: although, you don't seem to be forcing evaluation..

13:49 No, rest takes the whole remaining sequence (infinity - the first) :)

13:49 When you compare them, they are evaluated

13:49 chenglou: well, I thought at one point you'd hit a pointer equality when you compare both streams

13:50 just like you would, trying to deeply compare two trees

13:50 not-much-io: Clojure does not compare pointers, it compares values

13:51 chenglou: it does pointer comparison first though

13:51 not-much-io: Oh, sorry I am misinformed then.

13:51 chenglou: otherwise (= a a) would get stuck

13:51 not-much-io: Is it comparing the pointers of the list or the element?

13:52 chenglou: I guess the element, which is why my example doesn't terminate

13:52 not-much-io: Would seem that way.

13:52 chenglou: which is why I wonder it doesnt do it for the list, since it does something like that for subtrees for the other 3 collection types

13:53 not-much-io: It's actually a lazyseq

13:53 (type (range))

13:55 lazyseq might be a special case because it can be infinitely big. However I am just speculating. :)

13:55 chenglou: it can

13:55 but I guess this example makes slightly more sense:

13:55 (def a (range)) (def b (rest a)) (def c (cons 1 (nnext a)))

13:55 then (= b c)

13:56 you'd think that while going through the list it'd compare the list pointer first, hit the element 1, deeply compare, hit the rest of the list

13:56 not-much-io: I mean I am speculating about whether or not it is a special case implementation wise.

13:56 chenglou: then see the rest of the list point to the same place in a

13:56 not-much-io: I would think that (rest myseq) would return a new sequence

13:57 Clojure data structures are immutable

13:58 chenglou: yes I get that, but I was wondering why it didn't do it like this: "b's first cell is a new item, b's second third fourth... just point to that chunk in a"

13:58 not-much-io: (rest a) (conj a 1) etc. all create a new datastructure, though they reuse the old ones elements (for performance) it would make sense to consider them different

13:58 chenglou: it does, but I'm not explainining this correctly

14:00 not-much-io: As I understand you are surprised that two different datastructures with "shared" (as in under the hood shared) elements are not equal in term of pointers

14:00 dnolen: chenglou: it could probably behave the way expect, but sequence equality tests is eager on elements of the sequence, it doesn't special case equality testing of the tail

14:01 s/the way/the way you expect

14:02 chenglou: dnolen: I see. But does it make sense conceptually to compare the tail? Disregarding the perf benefits

14:02 like, should my above example be allowed:

14:02 `(def a (range)) (def b (rest a)) (def c (cons 1 (nnext a))) (= b c)`

14:02 certainly works for all the other collections

14:05 not-much-io: yeah I guess. I got that expectation from all other collections

14:12 Bronsa: chenglou: the problem there is that calling rest on (range) returns a new value each time

14:14 chenglou: if you replace (rest) with (iterate inc 0), it will terminate

14:15 chenglou: Bronsa: right. But calling (conj a 1) also returns a new value, for a vector a

14:16 If I'm not mistaken you could do (= (conj a 1) (conj a 1)) and it'll do pointer comparison for the a subtree

14:17 Bronsa: chenglou: clojure will do pointer comparison before doing value checks, this is why (def a (iterate inc 0)) (= (rest a) (rest a)) returns true in constant time

14:19 chenglou: Bronsa: what I was asking for is why lazy seqs didn't do tail comparison before cell comparison. Which dnolen said isn't implemented (yet? Or will never be? Dunno)

14:19 Bronsa: chenglou: because of implementation details in (def a (range)) (def b (rest a)) (def c (rest a)) b and c will never have an identical pointer

14:19 justin_smith: ~range

14:19 clojurebot: excusez-moi

14:19 Bronsa: this has to do with range being a chunked-seq

14:21 = isn't chunk-aware

14:21 justin_smith: ~range is |reply| because range is a chunked-seq.

14:21 clojurebot: c'est bon!

14:21 justin_smith: ~range

14:21 clojurebot: range is |reply| because range is a chunked-seq.

14:21 justin_smith: argh!

14:21 clojurebot: forget range

14:21 clojurebot: Huh?

14:21 justin_smith: ugh

14:22 ~forget range

14:22 clojurebot: I don't understand.

14:23 Bronsa: chenglou: what you described is how lazy-seq do work. this is why it works for (iterate inc 0). (range) is a special type of lazy-seq, backed by a chunked-seq as opposed to a normal seq, the tails will never be identical

14:25 amalloy: clojurebot: forget range is |reply| because range is a chunked-seq.

14:25 clojurebot: I forgot that range is reply because range is a chunked-seq.

14:25 justin_smith: thanks

14:25 amalloy: clojurebot: range is <reply> because range is a chunked-seq.

14:25 clojurebot: c'est bon!

14:25 amalloy: ~range

14:25 clojurebot: range is |reply| because range is a chunked-seq.

14:25 amalloy: uuuugh

14:25 justin_smith: ~clojurebot

14:25 clojurebot: clojurebot is a weirdo

14:26 amalloy: clojurebot: clojurebot is like an elephant: he never forgets a useless factoid

14:26 clojurebot: Roger.

14:26 chenglou: Bronsa: I see. I'll think a bit more about this, thanks

14:29 Bronsa: chenglou: I just looked at the lazy-seq source and it looks like I'm partially wrong, there's no pointer-equality check on the tails. I apologize

14:30 agreed this somewhat is surprising

14:31 chenglou: Bronsa: ah. Well tldr: I was wondering if value comparison of infinite lazy seqs should even be allowed (say, for get-in a value with a lazy seq key in a map)

14:31 justin_smith: Bronsa: then why is it that ##(let [n (iterate inc 0)] (= (rest n) (rest n))) works?

14:31 lazybot: ⇒ true

14:31 chenglou: if it's a map of vector -> int in the worst case I retrieve the int using a different vector and I only pay a small price for deep equality check on the key

14:31 but with lazy seq it's not just a small perf cost. It might loop forever

14:31 justin_smith: chenglou: well, there is no marker to tell you if a lazy seq terminates

14:32 ane: does requiring a namespace with :refer :all not import the defrecords

14:32 justin_smith: ane: require doesn't import classes

14:32 Bronsa: justin_smith: because = does pointer equality

14:32 chenglou: justin_smith: yes which is why I was thinking if conceptually you should just claim that you don't guarantee that behaviour at all (retrieving value in a map using a lazy seq, with value comparison)

14:32 justin_smith: Bronsa: oh, OK

14:33 Bronsa: justin_smith: the issue is with e.g. (let [n (iterate inc 0)] (= (cons 1 (rest n)) (cons 1 (rest n)))

14:33 justin_smith: ane: use :import for classes

14:33 Bronsa: ahh! OK

14:33 ane: justin_smith: oh yes. what's the best approach in tests? that?

14:33 Bronsa: which would work if lazy-seq used = internally, but they don't

14:33 they just invoke .equals skipping the pointer check

14:33 https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/LazySeq.java#L120-L121

14:33 justin_smith: ane: sure, or just use the class fully qualified with package, importing is just a syntax convenience

14:34 Bronsa: thanks, now I get it

14:34 ane: justin_smith: oh, bloody hell, yes

14:35 tolstoy: Anyone having issues with the latest cider snapshot? No repl appears.

14:36 justin_smith: tolstoy: did you delete all your elc files? when you upgrade cider versions your elc files are invalid

14:36 common cause of cider pain

14:36 tolstoy: Yeah, let me double check. My last version was 8.2. Didn't see anything odd over there (8.2 dir is gone).

14:36 justin_smith: caused by the fact that cider abi is a moving target, and elisp doesn't compile in a way that handles that nicely

14:37 tolstoy: this isn't just about cider elc files, it can be effected by eg. clojure mode elc files too in my experience

14:37 and of course you also need to restart emacs after a cider version upgrade

14:38 tolstoy: I do that the first sign of trouble. I learned at least one thing from using Windows in 1999 for six months!

14:38 justin_smith: haha

14:39 tolstoy: Works now (just killed ALL elpa stuff), but get: WARNING: CIDER requires nREPL 0.2.7 to work properly

14:39 justin_smith: tolstoy: did you upgrade your cider-nrepl dep to match your new cider.el version?

14:40 tolstoy: Ah, probably some other dep is pulling that in.

14:40 Yeah. Snapshot.

14:40 justin_smith: snapshots are by default only fetched daily, but there is a way to force update sooner

14:40 which I don't recall off the top of my head...

14:41 tolstoy: rm -rf ~/.m2

14:41 :)

14:42 justin_smith: yes, there is always the nuclear option, but there is a more elegant way to do it as well

14:42 tolstoy: lein -U?

14:43 Well, I only moved to the 0.9.0-snapshot today, so I'm not sure were the nrepl dep is. tools-nrepl?

14:44 justin_smith: oh, I don't know then - there is a way to look at the plugin deps tree

14:47 tolstoy: lein deps :tree reveals all, but I do't see a dep for nrepl with cider-nrepl. However, it does show up as a top level dep.

14:47 justin_smith: tolstoy: could be your lein version injecting it?

14:47 tolstoy: Yeah.

14:48 Bronsa: chenglou: FYI I'm opening a ticket with a patch that introduces the pointer check in seq comparison

14:54 tolstoy: justin_smith: Interesting. Although cider-nrepl (according to its project.clj file) requires 0.2.7, lein somehow does something to remove it and inject its own nrepl as a top-level dep.

14:54 pandeiro: tolstoy: you need to specify tools.nrepl in :dev :dependencies

14:54 tolstoy: Maybe that makes sense.

14:55 pandeiro: i don't understand lein well enough to know why :dev :dependencies take precedence over :dependencies

14:55 justin_smith: tolstoy: try providing it as a top level dep in your profiles.clj

14:55 pandeiro: maybe that is intuitive, i don't know

14:55 justin_smith: pandeiro: it's because ones a top level dep, and the other is transitive

14:55 pandeiro: the fix for this is known, hyPiRion posted it

14:55 justin_smith: top level takes precedence

14:55 pandeiro: both should be top level?

14:56 * pandeiro doesn't understand profiles, admittedly

14:56 justin_smith: pandeiro: not if one is coming from cider

14:56 that's why the fix is to explicitly specify the dep on your own top level

14:56 pandeiro: justin_smith: nope not what i'm saying

14:56 specifying in your own top level doesn't fix it

14:56 specifying it in your own top level *:dev* does

14:56 which to me is sort of odd

14:56 justin_smith: that's weird

14:57 tolstoy: Yeah, making it a dep of the :dev profile "solves" it.

14:57 pandeiro: link: https://github.com/technomancy/leiningen/issues/1840

14:57 black magic

14:57 hyPiRion: both are top level, but you shouldn't add tools.nrepl as a project dep only because you need the latest cider

14:57 pandeiro: hyPiRion: adding as non-dev does not fix

14:57 justin_smith: hyPiRion: profiles.clj :dev dep, of course

14:58 pandeiro: I think that is because of how profiles are merged and the effective profile is generated

14:58 a :dev dep overrides a regular dep

14:58 pandeiro: justin_smith: right, i imagined a merge was happening

14:58 hyPiRion: oh, yeah no, that's not happening. That'd be bad for repeatability

14:59 pandeiro: hyPiRion: what would be bad for repeatability?

14:59 being able to specify a project's tools.nrepl version explicitly?

14:59 hyPiRion: merging stuff from .lein/profiles.clj into the project.clj on dupe profiles

15:00 pandeiro: which one wins in the case profiles.clj has one :dev version and project has another?

15:00 hyPiRion: the most local one

15:01 pandeiro: project -> dep is less local than profiles -> :dev ?

15:01 tolstoy: I think the profiles.clj one wins. I just had that issue with an old lein-ancient.

15:01 hyPiRion: project.clj ← local profiles.clj ← user profiles.clj ← system profiles.clj

15:01 tolstoy: Speaking of which, tools.nrepl "0.2.8" is available.

15:02 gfredericks: with one less reflection warning :)

15:03 hyPiRion: But there is a regression related to transitive dependencies on tools.nrepl. The fix is going to be in 2.5.2

15:03 just fyi

15:03 Bronsa: chenglou: http://dev.clojure.org/jira/browse/CLJ-1679

15:26 chenglou: Bronsa: woohoo thanks!

15:30 seangrove: Bronsa: Very nice

16:42 Mandar: Hello!

16:42 I'm playing with transducers.

16:43 Is this code idiomatic? http://pastie.org/private/vgbhlsx0ugxpsfdorfajw

16:44 is there any better way to do null punning?

16:50 tomjack: if I was forced to do that for some reason, I'd consider def'ing a (fn [f] (fn [arg] (if (nil? arg) nil (f arg)))) separately

16:51 then (map (letting-nils-be f)) or whatever

16:52 (map #(cond-> % (some? %) f)) ?

16:53 basically it would generally seem un-idiomatic to me to define a special map which just modifies f, instead of passing a modified f to map

16:54 Mandar: tomjack: thank you

16:54 tomjack: if some particular case comes up a lot in a development, maybe makes sense, though (e.g. I guess keep came up enough in general clojure code to be added)

16:56 I guess your transducer may be dual to keep?

16:56 Mandar: I don't really know. Basically, I'm trying to avoid null pointer exceptions.

16:57 tomjack: (for amusement, I'm trying to think of an analogous name -- "leave"?)

16:57 Mandar: it's close to a Maybe, but I'm a FP newbie

16:58 basically, I tried to reimpliment this behaviour: https://github.com/clojure/algo.generic/blob/master/src/main/clojure/clojure/algo/generic/functor.clj

16:58 but with transducers

17:03 tomjack: either your custom transducer or just modifying the fn passed to map seem reasonable to me. if you go with a custom transducer, I guess it might be idiomatic to also provide the other arities for seqs, like clojure.core/map

17:03 dunno

17:03 maybe future libs will tend to drop that, and it's just there for backwards compat

17:04 I'd guess people will do it when it's easy

17:04 for something used not-too-often I'd prefer just calling map, because everyone knows what that means already

17:05 gfredericks: tomjack: #(cond-> % (some? %) f)) ===> #(some-> % f)

17:05 tomjack: oya!

17:06 Mandar: ^

17:06 Mandar: tomjack, gfredericks: thanks!

17:06 tomjack: I still am not on great terms with the new arrows

17:06 Mandar: I don't understand this code yet

17:06 I rewrote mine with a my-nil-pun fn

17:07 gfredericks: tomjack: I just yesterday realized I could use as-> at the end of a normal threading form to give the thing a name and do more complex stuff with it

17:07 tomjack: I think part of the reason I tend to avoid them is that I'm scared of all the unknown possibilities :)

17:07 Mandar: the only problem I see with this approach is the need to "protect" all the functions in the comp

17:07 "protect" meaning wrap them with my-nil-pun

17:08 doing a custom transducer lets me avoid that, but I don't know if it's a bad practice

17:08 tomjack: nah, if you're using it a lot, go for it, I think

17:09 gfredericks: well up

17:09 um

17:09 I mean

17:09 tomjack: s/think/opine/ :)

17:09 gfredericks: you don't have to think of it as writing your own transducer

17:09 (defn leave [f & args] (apply map #(some-> % f) args))

17:09 tomjack: :/

17:09 gfredericks: it's just a modification of map

17:10 tomjack: oh, you're targetting 1 and 2-arities?

17:10 gfredericks: and etc

17:10 3, 4, even 5

17:10 tomjack: but #(some-> % f) is 1-ary

17:11 gfredericks: okay not 3, 4, nor even 5

17:11 tomjack: but yeah, nice :)

17:11 gfredericks: I wonder if I can pass an infinite number of args to map

17:11 Mandar: gfredericks: your code is not using transducers anymore, is it?

17:11 gfredericks: Mandar: sure it is, because map does

17:12 Mandar: sorry, doing my best! :)

17:12 gfredericks: ,(apply map #(first %&) (repeat (range 5)))

17:12 clojurebot: eval service is offline

17:12 tomjack: better idea to sacrifice clojurebot than my emacs responsiveness :(

17:12 gfredericks: &(apply map #(first %&) (repeat (range 5)))

17:12 Mandar: haha

17:13 oddcully: clojurebot: this is an outrage!

17:13 clojurebot: In Ordnung

17:14 justin_smith: ~this

17:14 clojurebot: this was discussed in a loud bar

17:15 gfredericks: ~I

17:15 clojurebot: I will never forgive constantly polluting useful triggers like ~anyone by learning other irrelevant factoids to repeat instead

17:15 gfredericks: ~I

17:15 clojurebot: I will never forgive dumb

17:15 gfredericks: man me neither

17:16 tomjack: :)

17:23 Mandar: thank you very much guys

17:42 benjyz1: hi. quick question. how do I get "bar" out of this structure: {"foo" {"bar" 266}}

17:46 oddcully: benjyz1: (get-in {"foo" {"bar" 266}} ["foo" "bar"])

17:46 TEttinger: that will get the value associated with bar

17:47 benjyz1: thx. I'm a bit confused. this is an arrayhashmap, correct?

17:47 TEttinger: it's usually called a map

17:47 ,(def foobar {"foo" {"bar" 266}})

17:47 clojurebot: #'sandbox/foobar

17:48 oddcully: ,(get-in {"foo" {"bar" 266}} ["foo" "bar"])

17:48 TEttinger: ,(val foobar)

17:48 clojurebot: 266

17:48 #error{:cause "clojure.lang.PersistentArrayMap cannot be cast to java.util.Map$Entry", :via [{:type java.lang.ClassCastException, :message "clojure.lang.PersistentArrayMap cannot be cast to java.util.Map$Entry", :at [clojure.core$val invoke "core.clj" 1506]}], :trace [[clojure.core$val invoke "core.clj" 1506] [sandbox$eval54 invoke "NO_SOURCE_FILE" -1] [clojure.lang.Compiler eval "Compiler.java" 6...

17:48 TEttinger: the error was mine

17:48 oddcully: now it belongs to all of us

17:48 TEttinger: heh

17:49 if you want to specifically get the key "bar"

17:49 returning "bar", without knowing what key is there beforehand...

17:50 ,(-> foobar get "foo" keys)

17:50 clojurebot: #error{:cause "java.lang.String cannot be cast to clojure.lang.IFn", :via [{:type java.lang.ClassCastException, :message "java.lang.String cannot be cast to clojure.lang.IFn", :at [sandbox$eval95 invoke "NO_SOURCE_FILE" 0]}], :trace [[sandbox$eval95 invoke "NO_SOURCE_FILE" 0] [clojure.lang.Compiler eval "Compiler.java" 6784] [clojure.lang.Compiler eval "Compiler.java" 6747] [clojure.core$eval invo...

17:50 TEttinger: ,(-> foobar (get "foo") keys)

17:50 clojurebot: ("bar")

17:50 TEttinger: ,(-> foobar (get "foo") ffirst) ; if there is only one

17:50 clojurebot: "bar"

17:51 oddcully: i guess you take this way much to verbatimn

17:52 TEttinger: I've had cases where I want a key out of a data structure before

17:52 oddcully: OP was way unclear

17:52 TEttinger: yep

17:54 benjyz1: thx, got it.

17:54 TEttinger: get-in is great though

17:56 benjyz1: maybe another question :) what would be the easiest way to update a variable via an agent..

17:57 I have a function which calls a remote resource, and I want to have the update run once every second

17:57 TEttinger: most of the time clojure code doesn't need agents, though sometimes it really does

17:59 http://clojuredocs.org/clojure.core/agent start here I guess with the examples, I need to refresh my memory on this

17:59 benjyz1: I see. still trying to wrap my around refs and agents

17:59 TEttinger: atoms are nice and simple

17:59 justin_smith: benjyz1: an agent is a variable

17:59 benjyz1: it holds a value, you send it a function that updates it

18:00 TEttinger: they are simple, but not so great for things that require side effects in generating the value

18:00 TEttinger: ah

18:00 benjyz1: and starting a thread to update that state would be non-idiomatic?

18:01 justin_smith: benjyz1: sending to an agent uses the agent thread pool

18:01 benjyz1: in other words, it's already going to do the work in another thread

18:02 TEttinger: for example, if you were to use a request to a remote resource to update a value in an atom, retries would be a significant cost, so an agent matches that scenario more closely I think

18:03 TEttinger: ah ok

18:04 justin_smith: the maximum number of retries, worst case, is the factorial of the overlaps

18:04 so three overlapping changes could result in 6 calculations, 4 in 24 calculations, etc.

18:05 I think...

18:34 javjarfer: hi! Anyone can explain me why (map list {:1 1 :2 2} {:3 3 :4 4}) evaluates to (([:1 1] [:4 4]) ([:2 2] [:3 3]))??

18:34 lazybot: javjarfer: What are you, crazy? Of course not!

18:35 justin_smith: javjarfer: what did you expect it to do?

18:35 ,(map list [:a :b :c] [:d :e :f])

18:35 clojurebot: ((:a :d) (:b :e) (:c :f))

18:36 TEttinger: maps are unsorted, so that could be one source of confusion

18:36 javjarfer: justin_smith, (([:1 1] [:3 3]) ([:2 2] [:4 4]))

18:36 TEttinger: try using sorted-map there

18:37 hyPiRion: maps don't have any ordering by default.

18:37 TEttinger: (map list (sorted-map :1 1 :2 2) (sorted-map :3 3 :4 4))

18:37 ,(map list (sorted-map :1 1 :2 2) (sorted-map :3 3 :4 4))

18:37 clojurebot: (([:1 1] [:3 3]) ([:2 2] [:4 4]))

18:37 javjarfer: ahhhh, i see... thanks you so much, it was driving my crazy

18:37 TEttinger: there's also ordered from flatland

18:38 $google flatland/ordered clojure

18:38 lazybot: [flatland · GitHub] https://github.com/flatland

18:38 TEttinger: https://github.com/flatland/ordered

18:39 javjarfer: so, i think probably the third example from here: "https://clojuredocs.org/clojure.core/doseq" should be changed

18:42 TEttinger: javjarfer, that's a good catch

18:43 javjarfer: TEttinger, thank you! First contribution xD

18:44 TEttinger: (inc javjarfer)

18:44 lazybot: ⇒ 1

18:44 TEttinger: you has a karma, protect it, nurture it

18:44 (identity dnolen)

18:44 lazybot: dnolen has karma 21.

18:44 TEttinger: (identity amalloy)

18:44 lazybot: amalloy has karma 239.

18:45 javjarfer: TEttinger, ei! love that, there is a long way ahead

18:55 justin_smith: (identity justin_smith)

18:55 lazybot: justin_smith has karma 217.

19:11 justin_smith: ,(->> (all-ns) (mapcat (comp vals ns-interns)) (filter (comp :test meta)))

19:11 clojurebot: ()

19:11 justin_smith: still proud of that one-liner

19:12 (it will list all defined clojure.test tests)

19:15 TEttinger: &(let[a #(apply str(flatten %))r repeatedly R repeat p #(partition %(a(R %2 %3)))N rand-nth n #(a(N(concat(R %"")(mapcat p[1 1 2 2][13 5 8 2]%&))))v #(n 0"aioe""u""aiioieoa")w(fn[](let[s[(n 0"JYZKVGSMNPRH"""(a(for[V"EA"C"zkbhhmlrd"](str V C)))"")(r(N[1 1 2])#(do[(v)(n 0"zkssgnmpbhhll""dt""shth")]))]](a s)))Y(r 500 w)](a[(mapv(fn[u d](str u" "(N["begat""sired"])" "d". Then "))Y(rest Y))(last Y)" begat Rich, Amen."]))

19:15 lazybot: ⇒ "Mop sired Yib. Then Yib sired Elab. Then Elab begat Posok. Then Posok begat Gais. Then Gais begat Alieth. Then Alieth begat Yab. Then Yab begat Nam. Then Nam sired Zietah. Then Zietah begat Hail. Then Hail sired Sem. Then Sem begat Rioh. Then Rioh begat Koag. Then K... https://www.refheap.com/98743

19:15 justin_smith: haha, nice

19:17 TEttinger: http://ideone.com/zwnFAL is less obfuscated, it's used to generate english-like planet names for a friend's scifi game

19:17 it's meant to generate 10K unique names, but it runs out of memory on ideone if you try 10K there

19:17 it works fine locally

19:17 lazyseqs are awesome here

19:18 I generate an infinite list and call distinct on it, then take 10000

19:28 celwell: What's the best way to deref an atom from another namespace? @(resolve 'purple.dispatch/zq)

19:28 gfredericks: @purple.dispatch/zq

19:28 celwell: @dispatch/zq doesn't work

19:29 ah hmm thanks

19:29 gfredericks: how does it fail?

19:29 celwell: ill try the one you said

19:29 clojurebot: No entiendo

19:29 gfredericks: you can (:require [purple.dispatch :as dispatch])

19:29 if you want to use dispatch/zq

19:30 celwell: Well i am requiring dispatch, but @dispatch/zq failed. I'll try again

19:31 justin_smith: celwell: what is the error you get

19:32 celwell: java.lang.ClassCastException

19:32 clojure.lang.PersistentArrayMap cannot be cast to java.util.concurrent.Future

19:32 maybe it's unrelated

19:32 justin_smith: OK, you tried to deref a map

19:32 ,@{}

19:32 clojurebot: #error{:cause "clojure.lang.PersistentArrayMap cannot be cast to java.util.concurrent.Future", :via [{:type java.lang.ClassCastException, :message "clojure.lang.PersistentArrayMap cannot be cast to java.util.concurrent.Future", :at [clojure.core$deref_future invoke "core.clj" 2184]}], :trace [[clojure.core$deref_future invoke "core.clj" 2184] [clojure.core$deref invoke "core.clj" 2205] [sandbox$ev...

19:32 justin_smith: see, same error

19:32 celwell: oh, yeah i forgot i changed the strucutre of that atom, let me try again

19:36 ok, nvm...

19:38 Lewix: hello

19:39 justin_smith: greetings and salutations

19:41 arrdem: $GREETING

19:42 justin_smith: TEttinger: "It Came from Planet Cough"

19:44 TEttinger: I shared your planet generator with a friend, who discovered some dictionary words

19:44 also, "Chestoid must be where xenomorphs are from"

19:46 TEttinger: haha

19:47 justin_smith: "it's the fourth planet it the Chan system, best known for the troll-like hominids and their obsession with memes"

19:47 * arrdem beams up

19:47 TEttinger: nice

19:47 justin_smith: another good one from that list: "Tom"

19:47 clojurebot: Titim gan éirí ort.

19:48 TEttinger: I managed to parse out most if not all the swears

19:48 justin_smith: nice

19:48 TEttinger: Abbo keeps getting through, which is a slur for aboriginal people, typically australian

19:49 justin_smith: Ted is another one

19:49 TEttinger: I'm sure there's more ethnic slurs that it generates, clojure is such a racist generator

19:49 justin_smith: "the journey to planet Suck"

19:50 oh wow, "Foo" is in there

19:53 gfredericks: PSA: you can generate things from a regex with https://github.com/gfredericks/test.chuck#string-from-regex

19:54 arrdem: gfredericks: nice!

19:55 TEttinger: wow https://github.com/gfredericks/test.chuck/blob/master/resources/com/gfredericks/test/chuck/regex.bnf

19:55 (inc gfredericks)

19:55 lazybot: ⇒ 124

19:56 Seylerius: TEttinger: That a clojure karma system?

19:56 arrdem: Seylerius: yep

19:56 TEttinger: yep

19:56 Seylerius: Shiny.

19:56 arrdem: (inc gfredericks)

19:56 lazybot: ⇒ 125

19:56 arrdem: (identity arrdem)

19:56 lazybot: arrdem has karma 40.

19:56 arrdem: I need to lurk more

19:56 gfredericks: TEttinger: I learned a lot of disgusting things about jvm regexes while building that

19:56 TEttinger: (identity tekacs)

19:56 lazybot: tekacs has karma 0.

19:57 TEttinger: (identity TEttinger)

19:57 lazybot: TEttinger has karma 44.

19:57 TEttinger: gfredericks, I remember

19:57 stuff like the && grouping within [] right?

19:57 gfredericks: yeah that's the second worst

19:58 TEttinger: there's WORSE?

19:58 arrdem: there's always something worse

19:59 gfredericks: the worst thing I know of is that you can put a \Q\E virtually *anywhere* in a regex and it's a noop, even in the middle of things that should never be broken up

20:00 e.g.

20:00 &(re-matches #"\p{Alpha}+" "hello")

20:00 lazybot: ⇒ "hello"

20:00 gfredericks: &(re-matches #"\p{Al\Q\Epha}+" "hello")

20:00 lazybot: ⇒ "hello"

20:01 TEttinger: aaaaaagh

20:01 hyPiRion: omg what

20:02 arrdem: ew

20:02 justin_smith: gfredericks: oh my god that's an atrocity

20:02 it's like, war crime level stuff

20:03 gfredericks: there are some things you never want to have to tell your kids about

20:04 TEttinger: it's like \p{Ol} \p{Ot}

20:05 justin_smith: gfredericks: it seems the only rule is that one \Q\E can't be in the middle of another one

20:06 ,(re-matches #"\p{Al\Q\Q\E\Epha}+" "hello")

20:06 clojurebot: #<SecurityException java.lang.SecurityException: denied>

20:06 justin_smith: ,(re-matches #"\p{Al\Q\E\Q\E\Q\Epha}+" "hello")

20:06 clojurebot: "hello"

20:07 hyPiRion: ,(re-matches #"\p{\QAlEpha\E}+" "hello")

20:07 clojurebot: #<NoClassDefFoundError java.lang.NoClassDefFoundError: Could not initialize class java.util.regex.PatternSyntaxException>

20:07 hyPiRion: ,(re-matches #"\p{\QAlpha\E}+" "hello")

20:07 clojurebot: "hello"

20:07 hyPiRion: oh okay then

20:08 gfredericks: justin_smith: right

20:08 hyPiRion: gfredericks: but \Q can be inside a \Q, right?

20:08 gfredericks: oh I didn't expect that one o_O

20:08 yes it can

20:09 ,(re-matches #"\p\{\A\l\p\h\a}+" "hello")

20:09 clojurebot: #<NoClassDefFoundError java.lang.NoClassDefFoundError: Could not initialize class java.util.regex.PatternSyntaxException>

20:09 gfredericks: &(re-matches #"\p\{\A\l\p\h\a}+" "hello")

20:09 lazybot: java.util.regex.PatternSyntaxException: Unknown character property name {\} near index 2

20:09 gfredericks: &(re-matches #"\p{\A\l\p\h\a}+" "hello")

20:09 lazybot: java.util.regex.PatternSyntaxException: Unknown character property name {\A\l\p\h\a} near index 13

20:09 gfredericks: that should be equivalent afaik

20:09 er

20:09 maybe I'm confused

20:09 hyPiRion: well apparently not

20:09 although I would agree with you

20:09 gfredericks: gotta put a baby to bed

20:17 underplank: Hey all. Im using clj-http to build some unit tests. I have a route that Im wanting to make sure it returns a 404, but it seems like clj-http throws an exception. Is there a way I can get clj-http to not throw that exception?

20:18 ahh found it. {:throw-exception false}

20:18 should probably read the documentation

20:24 jeremyheiler: underplank: sometimes i wish that was the default

20:25 underplank: yeah… coming from python with the requests library where that is the default it is rather nice. I dont really like being forced into throwing an exception. But I guess you could argue that it forces good behaviour.

20:26 jeremyheiler: underplank: good behavior? eh. apis do waht apis do. hard to manage servers outside of your control

20:27 underplank: yeah…. thats very true. Its only good behaviour if you control both ends of the request. I get the feeling its a javaesque thing that managed to work its way into clj-http

20:27 *shrugs* at least there is a way out of it.

20:27 and apart from the the clj-http library is pretty damn good.

20:28 jeremyheiler: yeah, clj-http is a great lib

20:29 gfredericks: hyPiRion: it's because \Q...\E actually leaves alpha-ascii characters unchanged

20:29 it backslashes everything else

20:30 arrdem: why the hell...

20:30 hyPiRion: gfredericks: oh, so it's like a preprocessing step

20:30 gfredericks: hyPiRion: exactly

20:30 arrdem: because [a-zA-Z] normally have special meanings with a backslash but normal meanings without

20:30 with the exception of \p{...} that we were just playing with

20:30 I expect that's the only place where [a-zA-Z] have a special meaning and you can pull off that trick

20:31 &(re-matches #"\p{Al\Qph\Ea}+" "hello")

20:31 lazybot: ⇒ "hello"

20:31 gfredericks: hyPiRion: it *is* a preprocessing step

20:32 it's right here in fact: https://github.com/openjdk-mirror/jdk/blob/2ea10c722507cde99026faa56068e2295bb93f11/src/share/classes/java/util/regex/Pattern.java#L1567-1619

21:11 ben_vulpes: what does ^Foo mean in the context of a (def ... ) form?

21:11 in particular, looking at https://github.com/michaelklishin/pantomime/blob/master/src/clojure/pantomime/mime.clj#L17 - is that some sort of type annotation perhaps?

21:12 gfredericks: yes, a type hint

21:12 ben_vulpes: thanks gfredericks.

21:12 justin_smith: ,(meta (fn ^String [] "hi"))

21:13 clojurebot: nil

21:13 justin_smith: hrm...

21:13 gfredericks: ben_vulpes: it means e.g. that the call on line 33 can be figured out by the compiler

21:13 and doesn't have to use runtime reflection

21:13 justin_smith: gfredericks: it seems odd that the compiler couldn't infer that (Tika.) returns a Tika

21:14 gfredericks: justin_smith: it doesn't try very hard

21:14 justin_smith: yeah, I guess not

21:14 one k short of delicious

21:14 gfredericks: I assume the only inference it does is local

21:16 &(def justin_smith (Integer. 24))

21:16 lazybot: java.lang.SecurityException: You tripped the alarm! def is bad!

21:16 gfredericks: aw go away lazybot

21:26 ben_vulpes: https://github.com/michaelklishin/pantomime/blob/master/src/clojure/pantomime/mime.clj#L29 << regarding this protocol implementation, what sense does it make to extend the protocol for String, File, URL etc when in all of the implementations, everything is just run through a Tika (.detect Tika. *input*) call?

21:26 i ask out of ignorance, not arrogance :)

21:28 gfredericks: ben_vulpes: as opposed to just writing one function with that same implementation?

21:29 ben_vulpes: right.

21:29 that's the (naive, perhaps stupid) thing that i'd do.

21:29 gfredericks: it's the type hints again

21:29 at the jvm level I assume there are at least four different methods called "detect"

21:29 each with different type signatures

21:29 ben_vulpes: oho

21:30 gfredericks: and to avoid reflection you have to tell clojure (via type hints) which of those four you want to call

21:30 ben_vulpes: i think where i fail to grok is how the clojure type hint is conveyed to the jvm for the correct detect call

21:31 but -- outside of my scope atm. thanks, gfredericks!

21:31 gfredericks: np

21:54 justin_smith: ben_vulpes: I think it's more a question of if the clojure compiler can't tell when you compiler your code which class' method is to be called, it generates byte code that figures out the class, finds the method, and calls it

21:55 ben_vulpes: and that series of steps is why typehints make things faster

21:55 s/compiler/compile

22:35 ben_vulpes: makes sense, justin

22:35 justin_smith:

23:50 Lewix: can someone explain to me partial?

23:51 justin_smith: Lewix: it takes a function and N args, and returns a new function taking N fewer args

23:51 ,(def +2 (partial + 2))

23:51 clojurebot: #error{:cause "First argument to def must be a Symbol", :via [{:type clojure.lang.Compiler$CompilerException, :message "java.lang.RuntimeException: First argument to def must be a Symbol, compiling:(NO_SOURCE_PATH:0:0)", :at [clojure.lang.Compiler analyzeSeq "Compiler.java" 6732]} {:type java.lang.RuntimeException, :message "First argument to def must be a Symbol", :at [clojure.lang.Util runtimeEx...

23:51 justin_smith: oops

23:52 ,(def plus2 (partial + 2))

23:52 clojurebot: #'sandbox/plus2

23:52 justin_smith: ,(plus2 2)

23:52 clojurebot: 4

23:52 justin_smith: ,(plus2 2 2)

23:52 clojurebot: 6

23:53 Lewix: justin_smith: i fail to understand the purpose

23:53 justin_smith: you could just create a function

23:53 justin_smith: Lewix: that's what partial is for

23:53 it creates functions

23:54 Lewix: .(defn plus2 [n] (+ 2 n))

23:54 justin_smith: Lewix: that would break for my second example

23:54 Lewix: i see. thns

23:54 justin_smith: the idea is it is simple to use, and will be exactly like the original function with some arguments pre-provided

23:55 see also comp

23:55 which also just creates a function, which you could use defn or fn to create

23:55 Lewix: justin_smith: thanks

23:55 justin_smith: but comp can be clearer sometimes

23:55 n

23:55 Lewix: I gotta fo

23:55 justin_smith: err, I mean np

23:55 Lewix: go*

Logging service provided by n01se.net