2015-03-21
| 00:45 | arav93 | Goodnight! :) |
| 02:48 | aytch | #16 is a bit tricky |
| 02:49 | aytch | (= (__ "Dave") "Hello, Dave!") |
| 02:49 | aytch | the naive response (mine) was: str "Hello, " |
| 02:50 | aytch | but that doesn't evaluate as true, because it needs the "!" at the end |
| 02:52 | aytch | (= (str "Hello, " "Dave") "Hello, Dave") |
| 02:52 | aytch | true |
| 02:52 | aytch | (= (str "Hello, " "Dave") "Hello, Dave!") |
| 02:52 | aytch | false |
| 02:52 | TEttinger | yep |
| 02:52 | TEttinger | so you need to define a function there |
| 02:53 | aytch | yeah, I'm realizing that |
| 02:53 | aytch | it's just an exponential leap in difficulty |
| 02:53 | aytch | strings were only introduced in a question or two before |
| 02:53 | Seylerius | What's this: koans or something? |
| 02:53 | Seylerius | Or 4clojure? |
| 02:53 | aytch | Seylerius: 4clojure |
| 02:54 | TEttinger | (fn [name] (str "beginning " name " end")) |
| 02:54 | aytch | the title of the problem is "Hello World" |
| 02:54 | aytch | not "String Concatenation" |
| 02:54 | TEttinger | heh, hello world usually involves string concatenation as the second step |
| 02:54 | TEttinger | hello yourname |
| 02:55 | aytch | Absolutely, I agree |
| 02:55 | TEttinger | 4clojure definitely gets challenging quickly |
| 02:55 | TEttinger | have you reimplemented nth yet? |
| 02:55 | Seylerius | Annoyingly, 4clojure doesn't let me sign up. |
| 02:56 | aytch | I'm guessing no |
| 02:56 | TEttinger | that's one of the tasks that comes up around 20-30-something I think |
| 02:56 | TEttinger | it's a good challenge |
| 02:57 | amalloy | Seylerius: doesn't let you sign up? |
| 02:58 | Seylerius | Well, it let me sign up, but it keeps giving me an error on log in, and not a password error. |
| 02:58 | Seylerius | Okay, the problem seems to have been related to my username being capitalized. |
| 03:26 | aytch | I don't know who _pcl is, but all their answers match up with mine. And other people do some weird voodoo. |
| 04: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." |
| 04:13 | Seylerius | Totally off topic, but amusing. |
| 04:42 | Seylerius | 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. |
| 04:42 | Seylerius | Only seems to happen on the multi-test problems. |
| 04:50 | aytch | Seylerius: on the multi-test problems, there is only one correct solution. |
| 04:51 | aytch | the code will either pass all of the tests, or none of them, but the graphics are misleading. |
| 04: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. |
| 04:52 | Seylerius | It's very odd. |
| 04:52 | aytch | errr....there are multiple correct ways to implement the solution, but one solution must pass all tests. |
| 04:52 | aytch | I was asking about this a few days ago. |
| 04:52 | Seylerius | There must be some kind of bug in the rendering. |
| 04:53 | Seylerius | 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. |
| 04:53 | Seylerius | Very strange. |
| 04:53 | aytch | Which #? |
| 04:53 | Seylerius | I can't remember which one did it last. Maybe 17 or 18? I'll pipe up next time it does it. |
| 04:54 | Seylerius | 19 is... interesting. |
| 04:54 | Seylerius | Reimplement last. |
| 05:45 | Seylerius | http://sprunge.us/KYef |
| 05:46 | Seylerius | That's failing 4clojure #21: Nth Element |
| 05:46 | Seylerius | Oh, wait... |
| 05:46 | Seylerius | Nevermind |
| 06:28 | anti-freeze | Hey guys/girls. Does anyone have any issues with mongodb timing out? |
| 06: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 |
| 06: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" |
| 06:40 | deanm | Thanks anti-freeze, ill have a look at them as well |
| 06:46 | anti-freeze | Sorry, my mac went to sleep. Did I miss anything? |
| 06:48 | deanm | anti-freeze: nope, other than me saying thanks for the tips |
| 06:48 | anti-freeze | deanm: Ah, now that I remember. Probably the best intro is clojure for the brave and true |
| 06:48 | anti-freeze | deanm: Its free online |
| 06: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 |
| 06:50 | anti-freeze | deanm: The thing with clojure is that it needs perseverance. Its frustrating at first |
| 06:50 | anti-freeze | deanm: It was easier for me coming from a ruby background, where it was sort of pseudo functional |
| 06:51 | deanm | anti-freeze: Ok nice to hear that, i come from a Java background although i don't program these days anymore |
| 06: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 |
| 06:54 | anti-freeze | Anyone with suggestions on the mongodb issue? |
| 06:54 | anti-freeze | T'was working yesterday, I swear it |
| 07:12 | anti-freeze | 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)) |
| 07:21 | Glenjamin | ,`~'symbol |
| 07:22 | clojurebot | symbol |
| 07:22 | Glenjamin | anti-freeze: you can use ~'symbol inside the syntax-quote to get a bare symbole |
| 07:23 | Glenjamin | although in general it might be worth asking yourself if the macro provides any benefit over a function |
| 07:24 | anti-freeze | Glenjamin: Something like this? https://ideone.com/DjLZHZ I asked myself that, but it seems to provide more clarity |
| 07:26 | Glenjamin | (if-let [user (user-logged-in? params)] ...) |
| 07:26 | Glenjamin | you should be able to make that work with roughly the same code and no macro |
| 07:26 | Bronsa | anti-freeze: it's usually better to use gensyms rather than doing the ~' trick to get unqualified symbols |
| 07:26 | Glenjamin | maybe that should be called (logged-in-user params) |
| 07:27 | anti-freeze | Bronsa: I know, but i still want to access the found-user value to add his ID to session |
| 07:28 | Bronsa | anti-freeze: also you're trying to get runtime values at constant time |
| 07:28 | Bronsa | compile time* |
| 07:28 | anti-freeze | Bronsa: Well shit, how would you do it? |
| 07:28 | Glenjamin | the macro pasted is just some sugar for an if statement |
| 07:28 | anti-freeze | Ah, I give up... |
| 07:29 | Bronsa | anti-freeze: I'm talking about that cred/pass let. you don't seem to actually use those locals though |
| 07:29 | anti-freeze | Mongo doesn't work, I can't get simple authentication working... |
| 07: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 |
| 07:31 | anti-freeze | Bronsa: Yup, makes sense. I was swayed to clojure by all the preaching and what not of how amazing it is |
| 07:32 | Glenjamin | macros are a funny one, they're a headline language feature, but actually not used particularly often |
| 07:32 | Glenjamin | i overused macros quite a bit in my first clojure project |
| 07:33 | anti-freeze | How do I pass the then else branches then? |
| 07:33 | Glenjamin | just use a normal if form |
| 07:33 | anti-freeze | Glenjamin: Ah, makes sense |
| 07:33 | anti-freeze | makes cents? |
| 07:36 | Glenjamin | sense is correct |
| 07:36 | Glenjamin | something like this: https://www.refheap.com/98698 |
| 07:37 | anti-freeze | Glenjamin: Yea, that's what I changed it to. Hold on, let me do some copypasta |
| 07:38 | anti-freeze | Glenjamin: https://www.refheap.com/7c95d4846cc402dccbf87c6c5 |
| 07:38 | Glenjamin | yep, you can shrink it down a bit with some destructuring and more if-let, but thats the right idea |
| 07:41 | anti-freeze | Glenjamin: Alright man, thanks a bunch |
| 10:12 | anti-freeze | 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 | anti-freeze | 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 | gfredericks | 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 | anti-freeze | 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 | gfredericks | 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 | anti-freeze | One more question... for now. How would you test macros? |
| 11:00 | anti-freeze | Something like this: https://www.refheap.com/98704 |
| 11:01 | gfredericks | write some tests that use them maybe |
| 11:01 | gfredericks | is the whole point of these to be anaphoric? |
| 11:02 | gfredericks | 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 | justin_smith | 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 | anti-freeze | gfredericks: I guess the macros work just fine then |
| 11:38 | anti-freeze | 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 | not-much-io | 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 | not-much-io | 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 | justin_smith | OK |
| 12:30 | justin_smith | 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 | justin_smith | 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 | justin_smith | for uberjar reasons, among other reasons |
| 12:31 | justin_smith | 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 | not-much-io | Maybe I should not have included it, my bad. |
| 12:32 | not-much-io | 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 | justin_smith | 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 | justin_smith | not-much-io: oops, my bad :) |
| 12:34 | not-much-io | Okey, but is it really that expensive of a converion? :O |
| 12:34 | not-much-io | 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 | justin_smith | 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 | justin_smith | lol |
| 12:38 | not-much-io | A bug? |
| 12:38 | justin_smith | Execution time mean : -3.551208 ns |
| 12:38 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | justin_smith | turn on cpu profiling, then run your main function |
| 12:46 | justin_smith | or whatever other function you want to profile in detail |
| 12:47 | justin_smith | 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 | not-much-io | That was easy |
| 12:47 | justin_smith | yeah, it's easy, point and click |
| 12:47 | justin_smith | heh :) |
| 12:47 | not-much-io | Thanks! I would probabl have wasted a lot of time trying to use it :D |
| 12:47 | not-much-io | getMethods() are the mainusage |
| 12:48 | justin_smith | reflection! |
| 12:48 | justin_smith | try turning on reflection warnings vie project.clj |
| 12:48 | justin_smith | 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 | justin_smith | 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 | justin_smith | just make sure *warn-on-reflection* is true, whatever method works for your repl |
| 12:50 | justin_smith | 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 | justin_smith | (set! *warn-on-reflection* true) |
| 12:52 | justin_smith | that will work for the current thread, and any agents / futures created from that thread |
| 12:52 | justin_smith | if you use Thread though, it might not propagate |
| 12:53 | not-much-io | Nope, no threading currently |
| 12:53 | not-much-io | 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 | not-much-io | 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 | justin_smith | *warn-on-reflection* is about giving you guidance to where type hints are needed to speed up your code |
| 12:57 | justin_smith | 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 | justin_smith | 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 | not-much-io | Makes sense. |
| 12:58 | not-much-io | Thanks, learned alot. :) |
| 12:58 | justin_smith | this is especially true for math |
| 12:59 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | not-much-io | 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 | not-much-io | 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 | not-much-io | 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 | justin_smith | (set! *warn-on-reflection* :pedantic) |
| 13:15 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | justin_smith | Bronsa: maybe not |
| 13:19 | justin_smith | 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 | justin_smith | 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 | not-much-io | No, rest takes the whole remaining sequence (infinity - the first) :) |
| 13:49 | not-much-io | 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 | chenglou | 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 | not-much-io | (type (range)) |
| 13:55 | not-much-io | lazyseq might be a special case because it can be infinitely big. However I am just speculating. :) |
| 13:55 | chenglou | it can |
| 13:55 | chenglou | but I guess this example makes slightly more sense: |
| 13:55 | chenglou | (def a (range)) (def b (rest a)) (def c (cons 1 (nnext a))) |
| 13:55 | chenglou | then (= b c) |
| 13:56 | chenglou | 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 | not-much-io | 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 | dnolen | 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 | chenglou | like, should my above example be allowed: |
| 14:02 | chenglou | `(def a (range)) (def b (rest a)) (def c (cons 1 (nnext a))) (= b c)` |
| 14:02 | chenglou | certainly works for all the other collections |
| 14:05 | chenglou | 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 | Bronsa | 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 | chenglou | 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 | Bronsa | = 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 | justin_smith | clojurebot: forget range |
| 14:21 | clojurebot | Huh? |
| 14:21 | justin_smith | ugh |
| 14:22 | justin_smith | ~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 | Bronsa | 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 | chenglou | 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 | justin_smith | 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 | Bronsa | they just invoke .equals skipping the pointer check |
| 14:33 | Bronsa | 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 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | tolstoy | Yeah. Snapshot. |
| 14:40 | justin_smith | snapshots are by default only fetched daily, but there is a way to force update sooner |
| 14:40 | justin_smith | which I don't recall off the top of my head... |
| 14:41 | tolstoy | rm -rf ~/.m2 |
| 14:41 | tolstoy | :) |
| 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 | tolstoy | 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 | justin_smith | pandeiro: not if one is coming from cider |
| 14:56 | justin_smith | 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 | pandeiro | specifying in your own top level doesn't fix it |
| 14:56 | pandeiro | specifying it in your own top level *:dev* does |
| 14:56 | pandeiro | 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 | pandeiro | 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 | justin_smith | pandeiro: I think that is because of how profiles are merged and the effective profile is generated |
| 14:58 | justin_smith | 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 | pandeiro | 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 | hyPiRion | 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 | Mandar | I'm playing with transducers. |
| 16:43 | Mandar | Is this code idiomatic? http://pastie.org/private/vgbhlsx0ugxpsfdorfajw |
| 16:44 | Mandar | 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 | tomjack | then (map (letting-nils-be f)) or whatever |
| 16:52 | tomjack | (map #(cond-> % (some? %) f)) ? |
| 16:53 | tomjack | 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 | tomjack | 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 | Mandar | 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 | Mandar | 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 | tomjack | dunno |
| 17:03 | tomjack | maybe future libs will tend to drop that, and it's just there for backwards compat |
| 17:04 | tomjack | I'd guess people will do it when it's easy |
| 17:04 | tomjack | 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 | tomjack | 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 | Mandar | 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 | Mandar | "protect" meaning wrap them with my-nil-pun |
| 17:08 | Mandar | 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 | gfredericks | um |
| 17:09 | gfredericks | 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 | gfredericks | (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 | gfredericks | 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 | TEttinger | ,(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 | clojurebot | #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 | TEttinger | if you want to specifically get the key "bar" |
| 17:49 | TEttinger | returning "bar", without knowing what key is there beforehand... |
| 17:50 | TEttinger | ,(-> 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 | benjyz1 | 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 | TEttinger | 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 | justin_smith | benjyz1: it holds a value, you send it a function that updates it |
| 18:00 | justin_smith | 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 | justin_smith | benjyz1: in other words, it's already going to do the work in another thread |
| 18:02 | justin_smith | 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 | justin_smith | so three overlapping changes could result in 6 calculations, 4 in 24 calculations, etc. |
| 18:05 | justin_smith | 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 | justin_smith | ,(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 | TEttinger | ,(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 | TEttinger | $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 | TEttinger | (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 | justin_smith | (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 | TEttinger | it's meant to generate 10K unique names, but it runs out of memory on ideone if you try 10K there |
| 19:17 | TEttinger | it works fine locally |
| 19:17 | TEttinger | lazyseqs are awesome here |
| 19:18 | TEttinger | 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 | celwell | 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 | gfredericks | 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 | celwell | clojure.lang.PersistentArrayMap cannot be cast to java.util.concurrent.Future |
| 19:32 | celwell | maybe it's unrelated |
| 19:32 | justin_smith | OK, you tried to deref a map |
| 19:32 | justin_smith | ,@{} |
| 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 | celwell | 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 | justin_smith | TEttinger: I shared your planet generator with a friend, who discovered some dictionary words |
| 19:44 | justin_smith | 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 | 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 | justin_smith | 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 | TEttinger | (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 | TEttinger | 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 | gfredericks | e.g. |
| 20:00 | gfredericks | &(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 | justin_smith | 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 | justin_smith | ,(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 | gfredericks | yes it can |
| 20:09 | gfredericks | ,(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 | gfredericks | er |
| 20:09 | gfredericks | maybe I'm confused |
| 20:09 | hyPiRion | well apparently not |
| 20:09 | hyPiRion | 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 | underplank | ahh found it. {:throw-exception false} |
| 20:18 | underplank | 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 | underplank | *shrugs* at least there is a way out of it. |
| 20:27 | underplank | 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 | gfredericks | 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 | gfredericks | arrdem: because [a-zA-Z] normally have special meanings with a backslash but normal meanings without |
| 20:30 | gfredericks | with the exception of \p{...} that we were just playing with |
| 20:30 | gfredericks | I expect that's the only place where [a-zA-Z] have a special meaning and you can pull off that trick |
| 20:31 | gfredericks | &(re-matches #"\p{Al\Qph\Ea}+" "hello") |
| 20:31 | lazybot | ⇒ "hello" |
| 20:31 | gfredericks | hyPiRion: it *is* a preprocessing step |
| 20:32 | gfredericks | 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 | ben_vulpes | 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 | gfredericks | 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 | justin_smith | one k short of delicious |
| 21:14 | gfredericks | I assume the only inference it does is local |
| 21:16 | gfredericks | &(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 | ben_vulpes | 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 | ben_vulpes | that's the (naive, perhaps stupid) thing that i'd do. |
| 21:29 | gfredericks | it's the type hints again |
| 21:29 | gfredericks | at the jvm level I assume there are at least four different methods called "detect" |
| 21:29 | gfredericks | 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 | ben_vulpes | 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 | justin_smith | ben_vulpes: and that series of steps is why typehints make things faster |
| 21:55 | justin_smith | s/compiler/compile |
| 22:35 | ben_vulpes | makes sense, justin |
| 22:35 | ben_vulpes | 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 | justin_smith | ,(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 | justin_smith | ,(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 | Lewix | justin_smith: you could just create a function |
| 23:53 | justin_smith | Lewix: that's what partial is for |
| 23:53 | justin_smith | 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 | justin_smith | see also comp |
| 23:55 | justin_smith | 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 | justin_smith | n |
| 23:55 | Lewix | I gotta fo |
| 23:55 | justin_smith | err, I mean np |
| 23:55 | Lewix | go* |