2015-07-27
| 00:39 | justin_smith | (inc TimMc) |
| 00:39 | lazybot | ⇒ 101 |
| 01:15 | rhg135 | I wish you were joking |
| 04:44 | tgoossens | I have a function (defn blah [q] (.query conn q) (.close conn)) But i need the output of the query. Just us a let? |
| 04:45 | tgoossens | (the query output must be the return value, but close conn must come after obviously) |
| 04:56 | H4ns | tgoossens: (defn blah [q] (with-open [conn (open-conn)] (.query conn q))) may be an option |
| 04:56 | H4ns | tgoossens: but if query can't ever fail, use a simple let. |
| 04:57 | tgoossens | H4ns, thanks! |
| 07:46 | noncom | is destructuring considered a super low cost operation? I am reading this now http://blog.redlinernotes.com/posts/Lessons-from-cl-6502.html and that spot about destructuring caught my attention |
| 07:46 | noncom | sure, the post is about CL, but that made me to think about Clojure destructuring too |
| 07:47 | noncom | i suppose the implementations can be comparable.. |
| 07:55 | wasamasa | nope |
| 07:55 | wasamasa | do your own benchmarks and write a blog post |
| 08:29 | expez | (let [rdr (LineNumberingPushbackReader. (FileReader. path))] ...) I'm leaking file descriptors here, right? |
| 08:30 | schmir | expez: yes |
| 08:30 | schmir | expez: use the with-open macro |
| 08:31 | expez | mhmm |
| 08:35 | jeaye | I'd love some examples of non-trivial open source applications written in clojure so I can see how its idioms scale beyond the 20 line snippets that're so common in articles and tutorials. |
| 08:36 | jeaye | Ideally, the projects would hit a nice sweet spot between trivial and too much of a time investment to understand fully. Suggestions? |
| 08:44 | hellofunk | jeaye: well the Ring stuff is open source and fairly moderate in size, and is responsible for huge amounts of web sites and web apps in the world |
| 09:20 | Leonidas | hi |
| 09:20 | Leonidas | I have a macro like this: |
| 09:20 | Leonidas | (defmacro nest [sym] `(defmacro ~sym [& more#] (~sym more#))) |
| 09:21 | Leonidas | unfortunately, I'd like to actually unquote-splicing more# but it doesn't work, since it is not in a quasi quote |
| 09:21 | Leonidas | what can I do? |
| 09:23 | Leonidas | (apply does not work, since sym is most likely a macro) |
| 09:30 | expez | Leonidas: writing macros to write macros is super uncommon. In the event that you aren't actually an expert in a situation requiring this problem to be solved, you might consider that you've taken a wrong path somewhere further up in the decision tree :) |
| 09:31 | tsdh | Leonidas: That can't work. Basically, you want to know the actual arguments to your macro in its very own definition. |
| 09:31 | tsdh | expez: Macros writing macros is where the fun starts! ;-) |
| 09:33 | tsdh | expez: "Let over Lambda" is a really entertaining book doing all these sorts of multi-stage programming. |
| 09:34 | expez | tsdh: That's next on my reading list. Going through On Lisp now :) |
| 09:34 | tsdh | expez: Have fun! :-) |
| 09:34 | Leonidas | expez: it gets better. I'm writing a macro to write a macro to replace existing macros :-/ |
| 09:35 | tsdh | Leonidas: Awesome! |
| 09:35 | Leonidas | I have mastering clojure macros open too, but didn't get that much time to read it |
| 09:35 | Leonidas | LOL is on my reading list as well, though. |
| 09:37 | tsdh | Leonidas: Would it work to generate a macro with gets a collection `more` rather than having a varargs `more` parameter. If you need to, you can wrap it later to get to signature you want to have. |
| 09:42 | Leonidas | tsdh: the problem is that i have to call this ~sym macro I got passed with varargs |
| 09:45 | tsdh | Leonidas: Well, I think I simply don't get the context. |
| 09:47 | Leonidas | tsdh: I want to replace the macros of clojure.tools.logging with timbre macros. |
| 09:47 | Leonidas | (redef info) should re-def clojure.tools.logging.info with taoensso.timbre.info |
| 09:48 | Leonidas | If I replace them by hand-written macros if works just fine, but that's a lot of boilerplate |
| 09:49 | Leonidas | I have macros like (defmacro errorf [& more] `(taoensso.timbre/errorf ~@more)) |
| 09:50 | Leonidas | and would like to replace the errorf part with the argument of the redef macro |
| 09:56 | tsdh | Leonidas: Hm, I think you can just build the expansion by hand in the generated macro instead of using quasi-quote. |
| 09:56 | tsdh | (defmacro nest [sym] |
| 09:56 | tsdh | (let [args (gensym "more")] |
| 09:56 | tsdh | `(defmacro ~sym [& ~args] |
| 09:56 | tsdh | (list* '~sym ~args)))) |
| 09:57 | tsdh | Leonidas: Of course, the expansion is an infinite recursion here but that has been in your initial example, too. |
| 09:57 | Leonidas | tsdh: yes, I'm rewriting the symbol in my actual code, I just left it out to reduce the noise. |
| 09:57 | Leonidas | will try |
| 10:06 | Leonidas | tsdh: this fails, it says it can't take the value of the macro |
| 10:07 | Leonidas | oh, wait |
| 10:07 | Leonidas | I missed the quote |
| 10:09 | Leonidas | tsdh: whee, works. thank you a lot! I will have to study this code more in depth |
| 10:09 | tsdh | Leonidas: You're welcome! |
| 10:14 | xificurC | when I fire up lein repl and in-ns into my namespace and try to evaluate (+ 3 4) I get java.lang.RuntimeException: Unable to resolve symbol + in this context |
| 10:15 | tsdh | xificurC: That namespace hasn't been created properly before, i.e., with (ns your.ns ...). |
| 10:16 | tsdh | xificurC: Just (in-ns 'foo) creates the namespace without any bindings, i.e., you don't have all the clojure.core functions available and neither the bindings for java.lang classes. |
| 10:17 | tsdh | xificurC: So compile your namespace before changing to it, or require it from the repl. |
| 10:17 | xificurC | tsdh: I have this under src/xml_palettes/core.clj http://sprunge.us/RYjY |
| 10:18 | tsdh | xificurC: Yeah, then (require 'xml-palettes.core) before you (in-ns 'xml-palettes.core). |
| 10:18 | xificurC | tsdh: I see, thanks! |
| 10:18 | tsdh | xificurC: Or compile it in your environment, e.g., with C-c C-k in CIDER. |
| 10:18 | xificurC | using inf-clojure |
| 10:19 | tsdh | xificurC: That probably also has a binding for that but I don't know it. |
| 10:20 | xificurC | there's only C-c C-l for load file, not sure if that's the same |
| 10:21 | xificurC | yeah, that seems to work, thanks tsdh |
| 10:44 | dnolen | Bronsa: putting the finishing touches on bootstrapped ClojureScript. Will probably cut a pre-release if you could cut a tools.reader release that depends on it, that would be great, then people can do some testing before we cut a "final" ClojureScript one. |
| 10:48 | Bronsa | dnolen: sure, I'll cut a 0.10.0-alpha2 release -- don't think I can or even need to depend on a "future" version of cljs |
| 10:49 | Bronsa | it'll just work when pulled in by the new cljs and not work if pulled in by older versions |
| 11:11 | dnolen | Bronsa: ok, thanks! |
| 11:33 | noncom|2 | ,(concat '(1 2 3) {:a 1}) |
| 11:33 | clojurebot | (1 2 3 [:a 1]) |
| 11:33 | noncom|2 | ummmm... how can that be? |
| 11:34 | justin_smith | ,(seq {:a 1}) ; because this |
| 11:34 | clojurebot | ([:a 1]) |
| 11:34 | justin_smith | each entry is a two element vector |
| 11:34 | noncom|2 | ah! |
| 11:34 | noncom|2 | whoops, i was thinking i started sliding.. |
| 11:34 | noncom|2 | mixed up concat with conj.. |
| 11:34 | noncom|2 | it should be conj.. |
| 11:35 | justin_smith | ,(conj '(1 2 3) {:a 1}) |
| 11:35 | clojurebot | ({:a 1} 1 2 3) |
| 11:35 | noncom|2 | yeah, the order is backwards coz it's a list |
| 11:35 | justin_smith | ,(into '(1 2 3) {:a 1}) |
| 11:35 | clojurebot | ([:a 1] 1 2 3) |
| 11:35 | noncom|2 | hmmmm |
| 11:38 | hellofunk | justin_smith: didn't you say you are using http-kit to create websockets on a clojure server? how is that working out for you, anything i should be aware of when giving it a go after working on jetty? |
| 11:41 | justin_smith | hellofunk: it's been working fine for me |
| 11:41 | justin_smith | hellofunk: using sente to do it |
| 11:43 | hellofunk | justin_smith: is sente basically an abstraction over the http-kit websocket support? |
| 11:43 | justin_smith | hellofunk: it is protocol based, it abstracts http-kit and aleph iirc |
| 11:43 | justin_smith | it definitely has more than one supported backend |
| 11:44 | justin_smith | hellofunk: it lets you use websockets pretty much as if it were core.async |
| 11:44 | hellofunk | justin_smith: http-kit's docs suggest the websocket support is already quite easy without any other libraries; what is the advantage of adding sente to the mix? |
| 11:45 | justin_smith | hellofunk: you can directly provide clojure datatypes, it covers the serialization / deserialization, and hooks it up to core.async channels |
| 11:45 | justin_smith | also it has routing facilities |
| 11:46 | hellofunk | word |
| 11:46 | justin_smith | if you plan on making your own serialization / deserialization, doing your own routing and async hookup, it won't do much fore you I guess |
| 11:47 | justin_smith | on my next project I plan on trying the plain websocket route - I started with sente on this one and it just worked |
| 11:48 | hellofunk | "it just worked"... ? you mean Apple invented it? |
| 11:49 | justin_smith | it behaved as expected, at first |
| 11:49 | justin_smith | since then I have had to intrusively extend the serializer, and put my own routing layer on top... |
| 11:49 | justin_smith | stuff that makes me think I could start from scratch next time :) |
| 11:50 | justin_smith | hellofunk: it suffices if all you need is one button |
| 12:09 | arrdem | justin_smith: so um fair warning gonna harass you about websockets eventually (tm) |
| 12:10 | justin_smith | arrdem: cool |
| 12:11 | justin_smith | arrdem: one thing that worked out super awesome for our team was defining a single cljc file that defined routing for websocket messages going in both directions, it made hooking up new functionality much less of a tangle |
| 12:12 | arrdem | justin_smith: nice! |
| 12:12 | justin_smith | otherwise you get the whole "wait, which end do I define first, did I hook up all the fiddly details, oh man I have to edit five different files to make this work" freakout |
| 12:12 | arrdem | DRY |
| 12:13 | justin_smith | right - if you can define the routes that handle the request and the reply in one place, and then those refer to the functions called - it becomes a nice tree of code to implement with the router at the top, and you just walk down the tree until done |
| 12:13 | justin_smith | because otherwise with two way messaging you can get lost in a tangle of calls and responses way to easily, and lose track of details fast |
| 12:14 | justin_smith | (which is starting to happen to me now that we add kafka messaging alongside the websockets... ugh). |
| 12:14 | arrdem | sure. cool! a year or two ago I remember trying to play with websockets because some of the *coin exchanges exposed websocket data streams for transactions that I couldn't trivially consume |
| 12:14 | justin_smith | arrdem: turns out, managing two party communication is 1/3 as complex as managing 3 party communication |
| 12:14 | arrdem | now you have a distributed system. merry christmass. |
| 12:15 | justin_smith | arrdem: indeed, don't I know it |
| 12:15 | justin_smith | arrdem: I would have settled for a puppy, much less work |
| 12:15 | justin_smith | and my distributed system doesn't like belly rubs |
| 12:15 | arrdem | haha |
| 12:16 | arrdem | 's ok you have a distributed system that doesn't like belly rubs, my emitter has been giving me the stinkeye of late too :P |
| 12:16 | justin_smith | in all seriousness, we did intend to make a distributed system, but even when you go into it with the attitude "distributed systems are hard, this is going to be a lot of work" - - it turns out distributed systems are still harder than that |
| 12:17 | justin_smith | I've been perusing the Lynch Distributed Algorithms book for guidance / orientation, which helps |
| 12:18 | justin_smith | even if I am not implementing those algorithms from scratch (kafka and onyx do those things for me), it's good to see why things are arranged the way they are |
| 12:18 | arrdem | have you written up or even just found a good Clojure network simulator? |
| 12:18 | justin_smith | hmm |
| 12:18 | justin_smith | no, I have not |
| 12:18 | arrdem | I've got some fun ideas for vector clock based systems but I haven't actually built out anything to prototype them |
| 12:19 | arrdem | lol @ activation energy |
| 12:22 | justin_smith | arrdem: do jepsen or rymann do any of this, or do they do their simulation / interferance on an OS level? |
| 12:22 | justin_smith | sorry, riemann |
| 12:24 | arrdem | justin_smith: I can't quite tell but it looks like jepsen is probably a framework for doing exactly that sort of simulation testing |
| 12:25 | arrdem | justin_smith: presumably it is and once aphyr gets the talk "done" it'll stablilize :P |
| 12:25 | justin_smith | arrdem: I think it uses linux kernel / system calls to do the "simulation" - so it is OS level simulaiton |
| 12:25 | justin_smith | which may suffice ? |
| 12:26 | justin_smith | arrdem: https://github.com/aphyr/jepsen/blob/master/doc/lxc.md |
| 12:27 | arrdem | justin_smith: right so this is a framework for spinning up and then partitioning networks of vms |
| 12:27 | justin_smith | exactly |
| 12:28 | arrdem | hum. I think that for what I'm after local testing with something akin to core.async to simulate a network buss would be sufficient. |
| 12:28 | arrdem | anyway. pleny of other yaks |
| 12:57 | clojer_ | How do I get a Reagent app uberjar to serve gzipped js files? |
| 13:12 | Seylerius | Hrm. How do I turn an clj-time interval into a decimal fraction of days? |
| 13:13 | Seylerius | 596.23 days, for example. |
| 13:24 | joegallo | Seylerius: (in-days the-interval)? |
| 13:28 | amalloy | justin_smith: recalling last week's discussion about the oddities of clojure.core/destructure, what do you suppose are the values of a/b/c/d in (let [[a :as b c :as d] (range 5)]) (and, are any of them unbound?) |
| 13:29 | justin_smith | amalloy: oh, wow... umm a would be 0 I think, b would be the range... c and d nil? |
| 13:29 | justin_smith | wild guess there |
| 13:29 | amalloy | c and d are unbound, it turns out |
| 13:29 | amalloy | but you don't get an error from the let-binding if you don't try to use c or d |
| 13:29 | amalloy | you can just write any old junk after an :as |
| 13:30 | amalloy | ,(let [[x y :as z 1 2 3 whatever] (range 5)] [x y z]) |
| 13:30 | clojurebot | [0 1 (0 1 2 3 4)] |
| 13:30 | justin_smith | amalloy: I'm surprised how close I was to right there- but also surprised that those values are unbound rather than nil |
| 13:31 | justin_smith | (given the other behaviors I have seen) |
| 13:31 | amalloy | justin_smith: destructure just stops processing the binding list once it gets to :as |
| 13:31 | amalloy | so it doesn't create bindings for those |
| 13:31 | Bronsa | unbound is slightly better than nil in this case |
| 13:31 | justin_smith | amalloy: hidden comment feature! |
| 13:31 | Bronsa | at least you get a runtime error rather than nothing |
| 13:31 | amalloy | yeah |
| 13:31 | justin_smith | Bronsa: yeah, I would rather have seen unbound errors in the other misuses of destructure |
| 13:32 | kavkaz | Does the condition map in a function, specifically for the :pre key, have an effect? Or is it purely for documentation? |
| 13:32 | justin_smith | I guess silently ignoring erroneous input can be one drawback in the general "data structures before functions, functions before macros" thing - where unused parts of the data structure can look like functionality you were intending to invoke but never get evaluated |
| 13:33 | Bronsa | ,((fn [a] {:pre [(string? a)]} a) 1) ;; kavkaz |
| 13:33 | clojurebot | #error {\n :cause "Assert failed: (string? a)"\n :via\n [{:type java.lang.AssertionError\n :message "Assert failed: (string? a)"\n :at [sandbox$eval50$fn__51 invoke "NO_SOURCE_FILE" 0]}]\n :trace\n [[sandbox$eval50$fn__51 invoke "NO_SOURCE_FILE" 0]\n [sandbox$eval50 invoke "NO_SOURCE_FILE" 0]\n [clojure.lang.Compiler eval "Compiler.java" 6792]\n [clojure.lang.Compiler eval "Compiler.java" 6... |
| 13:33 | justin_smith | kavkaz: it has an effect if assertions are on, whether assertions are on is a vm level thing outside clojure's control |
| 13:33 | Bronsa | justin_smith: there's actually clojure.ore/*assert* |
| 13:33 | justin_smith | Bronsa: oh, wow |
| 13:34 | justin_smith | Bronsa: I thought it just used the vm config. But anyway I think the same takeaway is there - it's a thing that a user can turn off, so don't rely on assertions enforcing correctness, but do use them to detect bad code. |
| 13:35 | Seylerius | joegallo: I'll try that, thanks. |
| 13:35 | kavkaz | Bronsa, justin_smith: thanks! saw somebody use it in book. Can i put anything inside that map? Before I knew it was a condition map, i played around with a function and added :hi "hi" to the map. Can I use this like let? |
| 13:35 | kavkaz | I know it wouldn't be idiomatic and it will be confusing, but in theory I could do that right? |
| 13:36 | justin_smith | kavkaz: it pays attention to the :pre and :post keys - you can put anything in there, but why? |
| 13:36 | amalloy | that wouldn't be anything like let. it wouldn't create any bindings |
| 13:36 | justin_smith | kavkaz: it isn't like let because it doesn't create bindings you can usefuly refer back to does it? |
| 13:36 | Bronsa | yeah sure -- there's actually a vm thing for the `assert` statement in java but we don't use that |
| 13:37 | justin_smith | Bronsa: I had figured that since it was raising an AssertionError, turning off assertions would turn it off |
| 13:37 | kavkaz | Oh i see, i put a string in there "bound" to a key, it compiled but I guess I never tried referring to that string |
| 13:53 | amalloy | i just tride building clojure/clojure for the first time in ages, and `mvn test` prints a bunch of errors about edn round-tripping failures. is that command supposed to work, or is that the wrong way to run clojure's tests? |
| 13:57 | arrdem | I believe that is indeed the official test command |
| 13:57 | Bronsa | amalloy: it's supposed to work |
| 13:58 | Bronsa | amalloy: maybe you had some stale files in target/ or an unclean git history? |
| 13:59 | amalloy | the git history is clean, i'm exactly on latest master. stale files in target sounds like it is probably right |
| 14:00 | amalloy | Bronsa: thanks, it all works fine after deleting target. i wonder how old that stuff was |
| 14:02 | joefromct | can anyone recommend a current book, article, or blog in regards to getting started with clojure over hadoop processing? seems everything is a few years old at this point... i've looked at clojure-hadoop, netflix pigpen, and cascalog. |
| 14:02 | joefromct | any tips appreciated. |
| 14:02 | arrdem | amalloy: probably pre-1.7 printing changes |
| 14:19 | justin_smith | joefromct: I know yieldbot has a few nice things. And big picture if you need distributed computation but might not actually specifically need hadoop, check out onyx |
| 14:25 | joefromct | justin_smith: that looks interesting, thanks i'll check it out. |
| 14:26 | justin_smith | joefromct: I've been experimenting with onyx and like it so far. The project lead is pretty responsive if you need help getting oriented. |
| 14:41 | pepijndevos | How the hell do I make a Hiccup select box? There is this select-options thing, but I can't get it to work, and I can;t find any examples. |
| 14:42 | arrdem | Has anyone messed with a "server" command mode for leiningen where you get a repl for issuing lein commands rather than a Clojure repl? |
| 14:43 | justin_smith | arrdem: lein 1.x had this and 2.x does not for reasons which are reasonable but forgotten by me |
| 14:43 | justin_smith | arrdem: of course you can do that easily with boot |
| 14:43 | arrdem | justin_smith: sure. ok. maybe time for some mail list archeology later then. |
| 14:44 | justin_smith | arrdem: hyPiRion might remember why long running lein executing various tasks stopped being a thing |
| 14:44 | amalloy | arrdem: it was called lein interactive, if this helps your searching |
| 14:46 | arrdem | https://groups.google.com/forum/#!msg/leiningen/whV-VUdKSWM/O9H7cUQbGF8J |
| 14:49 | reuigerg | Hi! I'm making my first web service in Clojure. I made non-web projects in the language, and would like to use it for web stuff too. My hope is not having to go back to Django and Rails for these things. I'm struggling with Authentication. There are 2 libraries in CLJ for doing this, Friend and Buddy. Does anyone have any experience with these, and can tell which one will get you off the ground with the least amount of wor |
| 14:49 | reuigerg | elegant solution, but want to have an "account" feature (log in, register, keep a token) before bedtime today. So: What is the leanest way to do authentication in Ring/Compojure webapps? |
| 14:56 | justin_smith | reuigerg: the first part of your question was too long and it got cut off by IRC |
| 14:57 | reuigerg | Sorry - I'll re-ask more briefly |
| 15:00 | reuigerg | I'm making my first Clojure web (& postgres) project (having done non-web stuff). It requires user accounts (login with email+password, save a token to cookie, validate that token on every request). From what I can see, most people either use the lib Friend or Buddy. I'd like to implement this feature as soon as possible, so my question is: what is, in your experience, the leanest and quickest way to implement this in a ri |
| 15:00 | reuigerg | webapp? What are some great texts to read, in order to understand how to do this? Like a screencast or tutorial. |
| 15:03 | justin_smith | reuigerg: first half was cut off again |
| 15:03 | reuigerg | Shortest version: What is the quickest and leanest way to do user account & authentication in Ring/Compojure webapps? Are there any good guides for this? |
| 15:03 | reuigerg | Sorry :( |
| 15:03 | justin_smith | reuigerg: it's OK, wanted to make sure we weren't missing essential stuff... |
| 15:04 | justin_smith | I looked into friend and just ended up whipping something up with ring-session and bcrypt, I've talked to a few other people with similar stories |
| 15:05 | reuigerg | I see. I'm wrestling with Friend as well at the moment, but don't know how to make sense of it all yet. Worried about doing it all vanilla, because it seems easy to screw up (and then screw all users over) |
| 15:06 | justin_smith | reuigerg: a) don't store a password, store the bcrypt hash b) use the bcrypt hash-verification function on login |
| 15:07 | justin_smith | and do this over ssl, of course |
| 15:07 | justin_smith | there's not a lot of moving parts there |
| 15:08 | justin_smith | if you need the extra workflow stuff from friend, use it of course. But for simpler setups it seems more trouble than it's worth. |
| 15:09 | reuigerg | justin_smith: Ok, thank you. Maybe that is the easiest way. Btw, I've seen you answer questions a lot in #clojure. Thanks for that! Appreciate it |
| 15:10 | expez | reuigerg: you might want to check out Buddy |
| 15:10 | justin_smith | I try, hopefully it's helpful |
| 15:10 | expez | reuigerg: https://github.com/funcool/buddy it's easier to pick and choose from buddy than it is from friend |
| 15:11 | reuigerg | justin_smith: how do you then check authentication on each request? I guess you always want to assure that the user is logged in. Do you save a session somewhere, or generate that (like in JWT)? |
| 15:11 | ed-g | reuigerg, I found a useful Friend tutorial @ https://github.com/ddellacosta/friend-interactive-form-tutorial |
| 15:12 | ed-g | reuigerg, I'm in the same boat you are. |
| 15:12 | justin_smith | reuigerg: jwt if we load balance (WARNING: default for jwt is to accept the encoding type as provided by client, one of the encoding types is "none" which allows them to provide auth in plaintext and is a huge security hole. Be sure you are whitelisting encoding methods server side) |
| 15:12 | reuigerg | expez: I see. Have been fighting with both Friend a couple of days, but havent figured it out |
| 15:12 | reuigerg | ed-g: cool! Thank you! Will read :) |
| 15:12 | justin_smith | reuigerg: if not using load-balancing, it's easy to have a server side in-memory session and require users to log back in after restarts |
| 15:14 | reuigerg | justin_smith: valid point |
| 15:17 | reuigerg | To you all who answered: thank you for helping a guy confured in an unfamiliar but exciting labyrinth of parens |
| 15:17 | cmarques | What's the preferred way of doing function stubbing when testing with clojure.test? |
| 15:19 | justin_smith | cmarques: best option, if you can pull it off, is to parameterize your code so you don't actually need to mock a specific var |
| 15:19 | justin_smith | otherwise there is with-redefs |
| 15:19 | amalloy | arrdem: you mentioned wanting to see my reimplementation of destructure. not that i expect it would ever get in, but the patch is at https://www.refheap.com/bdd1b19b6f60d21fc00e48e0e. i'm no really sure about the change to use next/first instead of nth; it'll be faster for seqs, and involve one wasted allocation for vectors |
| 15:20 | cmarques | justin_smith my specific case: I have a ring handler that calls a function foo that accesses the database. When testing the handler, I don't want to access the database. |
| 15:20 | justin_smith | cmarques: yeah, sounds like a good use case for with-redefs, unless you want a test db |
| 15:21 | justin_smith | cmarques: or you could have a middleware that supplies the db access function, which could be replaced with a non-db replacement while testing |
| 15:21 | justin_smith | whatever is cleaner in your use case |
| 15:22 | cmarques | justin_smith I am looking at with-redefs, seems to do what I need. Thanks for your help! |
| 15:24 | justin_smith | cmarques: as with any with-foo, be super careful about scope+ laziness leading to unexpected results |
| 15:24 | justin_smith | basically, either do your work inside the body, or force anything lazy before returning it |
| 15:25 | cmarques | I see, I'll keep that in mind! |
| 15:34 | crazydiamond | Hi. If I have (defn f [a b] (+ a b)), can I get '(+ a b), i.e. the body? |
| 15:34 | justin_smith | no |
| 15:35 | justin_smith | well, you can from #'f |
| 15:35 | crazydiamond | so, I should have code in a first place |
| 15:35 | justin_smith | but not from the value of #'f |
| 15:35 | amalloy | justin_smith: not even from #'f really |
| 15:35 | hiredman | depends what you mean by having (defn f [a b] (+ a b)) |
| 15:35 | justin_smith | ,(defn f [a b] (+ a b)) |
| 15:35 | hiredman | if you run that through eval (the compiler) then you don't have it |
| 15:35 | clojurebot | #'sandbox/f |
| 15:35 | hiredman | you have compiled byte code |
| 15:35 | justin_smith | ,(:source #'f) |
| 15:35 | clojurebot | nil |
| 15:35 | justin_smith | ahh |
| 15:36 | crazydiamond | aha |
| 15:36 | hiredman | if you actually have (defn f [a b] (+ a b)) then |
| 15:36 | amalloy | justin_smith: source reads the .clj file |
| 15:36 | justin_smith | right, I forgot |
| 15:36 | crazydiamond | I tried to get it with clojure.repl/source and got "Source not found" |
| 15:36 | hiredman | ,(-> (defn f [a b] (+ a b)) (nth 3)) |
| 15:36 | clojurebot | #error {\n :cause "nth not supported on this type: Var"\n :via\n [{:type java.lang.UnsupportedOperationException\n :message "nth not supported on this type: Var"\n :at [clojure.lang.RT nthFrom "RT.java" 881]}]\n :trace\n [[clojure.lang.RT nthFrom "RT.java" 881]\n [clojure.lang.RT nth "RT.java" 847]\n [sandbox$eval72 invoke "NO_SOURCE_FILE" 0]\n [clojure.lang.Compiler eval "Compiler.java" 68... |
| 15:36 | hiredman | ,(-> '(defn f [a b] (+ a b)) (nth 3)) |
| 15:36 | clojurebot | (+ a b) |
| 15:37 | crazydiamond | ok, got it. thanks! |
| 15:37 | TEttinger | ,(-> '(defn f [a b] (+ a b)) (nth 3) eval) |
| 15:37 | clojurebot | #error {\n :cause "Unable to resolve symbol: a in this context"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.RuntimeException: Unable to resolve symbol: a in this context, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyze "Compiler.java" 6611]}\n {:type java.lang.RuntimeException\n :message "Unable to resolve symbol: a in this context"\n ... |
| 15:37 | TEttinger | ,(-> '(defn f [a b] (+ a b)) eval) |
| 15:37 | clojurebot | #'sandbox/f |
| 15:37 | TEttinger | ,(f 3 4) |
| 15:37 | clojurebot | 7 |
| 15:37 | TEttinger | nice clojurebot! |
| 15:37 | TEttinger | (inc hiredman) |
| 15:37 | lazybot | ⇒ 81 |
| 15:37 | hiredman | clojurebot: source? |
| 15:37 | clojurebot | source is http://github.com/hiredman/clojurebot/tree/master |
| 15:38 | hiredman | meh |
| 15:38 | hiredman | source is misleading |
| 15:47 | devnewb | hi all can anyone point me to an example of core.async ‘async/reduce’ in the wild, I can’t seem to aggregate my channel results into it.. I only get the last item |
| 15:48 | cmarques | justin_smith: Just solved the problem with with-redefs, works beautifully! Thank you! |
| 15:49 | devnewb | I’ve tried something like : (println "merged : " (async/<!! (async/reduce merge {} some-chan))) |
| 15:50 | justin_smith | cmarques: (async/reduce conj [] some-chan) is less interesting, but has fewer gotchas |
| 15:50 | justin_smith | cmarques: cool, glad to hear it worked out |
| 15:55 | devnewb | justin_smith, thanks, I already had that and it worked.. I was just trying to puzzle through the merge gotchas… thanks for the pointer though |
| 15:57 | justin_smith | devnewb: if conj worked and merge did not, I would look at the hash maps you are merging - maybe you want merge-with and not merge |
| 15:57 | justin_smith | ,(merge {:a 0} {:a 1} {:a 2}) |
| 15:57 | clojurebot | {:a 2} |
| 15:57 | justin_smith | ,(merge-with + {:a 0} {:a 1} {:a 2}) |
| 15:57 | clojurebot | {:a 3} |
| 15:58 | devnewb | justin_smith: ah, thanks |
| 15:58 | devnewb | I was misunderstanding merge |
| 15:58 | devnewb | should have read the docs more |
| 16:09 | justin_smith | (inc docs) |
| 16:09 | lazybot | ⇒ 2 |
| 16:12 | vas | Hi Guys, I finally have my site live, the login system is just an "email you a link and you click it" ... it works (!) but when I first run the app it takes an unusually long amount of time to send the first email... any ideas as to how I can get more "what's going on under the hood" feels/insight? |
| 16:17 | vas | maybe it's a mail issue o.O |
| 16:20 | oddcully | vas: if you send your email synchronously, then there could be an overhead for establiching everything to make the mail go out |
| 16:20 | oddcully | vas: things like authing and reverse lookups on hosts etc |
| 16:21 | vas | oddcully: you know it sounds like that might be it. i'm using postal to send mail. i wonder if there's a way to do it asynchronously and just kinda "throw it on the queue" |
| 16:24 | justin_smith | vas: you could put the send in a future (and be sure to come back and check the future for errors later) |
| 16:25 | dagda1_ | when working with a binary tree clojure devs use zippers? |
| 16:25 | dagda1_ | would clojure devs |
| 16:26 | justin_smith | dagda1_: that is one approach. If you are just navigating one a recursive function might suffice. |
| 16:26 | amalloy | depends what you are doing with the tree. usually not |
| 16:26 | dagda1_ | amalloy would you just use a map? |
| 16:26 | amalloy | conveniently the same answer applies: depends what you are doing with the tree. usually not |
| 16:27 | vas | oddcully: I added a line to my apache config "ProxyBadHeader Ignore" and it works... =| |
| 16:28 | vas | justin_smith: that's a cool idea. The future part of that makes sense to me, but how would one "come back to check errors" ? |
| 16:29 | dagda1_ | well that cleared things up......glad I asked |
| 16:29 | justin_smith | vas: put the future somewhere where you can access it later (perhaps an atom). You can test if it has returned with realized? |
| 16:29 | justin_smith | vas: if you access the value, it will throw the exception it caught, if any |
| 16:30 | justin_smith | so you can put your exception handling code in a function (maybe another future...) that comes back later and checks that it was successful |
| 16:31 | justin_smith | of course this assumes that it is reasonable to fire-and-forget your email sending while handling a request, and come back and deal with errors (if any) later |
| 16:32 | oddcully | vas: also depends, if you really need to tell the user, that you have sent the email. you can as well just tell the user, that you have done so and just records failures on your server for forensics |
| 16:32 | amalloy | dagda1_: the point is, what you use to work with a data structure depends on what you want to do. you could as well ask "how would you work with an integer? would you just use division?" well, probably not, but i can't be sure without knowing your goal |
| 16:34 | oddcully | vas: i also don't see how the apache config there relates; i only know, that javamail takes a short time to establish stuff. it's in general a good idea to offload sending mails to a queue/worker/... since there can be errors all over the place |
| 16:37 | dagda1_ | amalloy I am just tyring to solve this problem https://www.hackerrank.com/challenges/swap-nodes and to me a zipper seemed a good approach which made me wonder what somebody with more experience in clojure would use |
| 16:41 | amalloy | you might build the tree with a zipper. their input format is pretty hostile. the swapping seems like it would be easier to do with just some recursion of your own? i dunno, maybe you could use a zipper for that easily enough too |
| 16:42 | justin_smith | dagda1_: hell, I think that flip could be done with get-in / update-in |
| 16:42 | justin_smith | granted, that's after a skim, maybe that's too simple |
| 16:42 | amalloy | justin_smith: for *all* nodes at depth N? seems a bit tricky |
| 16:43 | justin_smith | ahh, yeah, so maybe recursion + update-in, right |
| 16:53 | crazydiamond | Hwdy get all subsequences of a vector? Like [1 2 3] -> [[1 2 3] [1 2] [2 3] [1] [2] [3]]? |
| 16:57 | amalloy | you forgot [] |
| 17:02 | crazydiamond | amalloy, where? :) |
| 17:02 | crazydiamond | ah, yep |
| 17:02 | crazydiamond | 0-length one |
| 17:06 | amalloy | anyway crazydiamond, of course there is nothing like this built in. you can build your own pretty easily with the use of clojure.core/partition |
| 17:07 | crazydiamond | amalloy, thanks. I was thinking about two nested loops |
| 17:07 | amalloy | in a functional language, you usually want to be thinking about recursion instead |
| 17:08 | amalloy | (here, as in many cases, there is a better solution that involves combining some other built-ins, but doing it recursively first is a good exercise) |
| 17:10 | crazydiamond | in the meantime, I wonder why Rich Hickey made that (loop ...) explicit, but not e.g. transformation of tail recursion into loop/recur style |
| 17:10 | crazydiamond | (though, I bet one might construct complex enough macro for it) |
| 17:14 | scriptor | crazydiamond: as I understand it, they wanted recur to be explicit so that it could be used as desired |
| 17:14 | scriptor | otherwise, since there's no actual TCO |
| 17:15 | scriptor | it'd be somewhat unclear when a function gets optimized or not |
| 17:15 | crazydiamond | aha, make sense |
| 17:17 | akkad | crazydiamond: jvm limitations? stack overflow |
| 17:29 | crazydiamond | amalloy, do you think this is good enough solution for my problem? or overcomplicated? http://dpaste.com/2DYXPPW |
| 17:31 | amalloy | you didn't like the partition suggestion? it is a much simpler way to write your all-subvecs-of... |
| 17:31 | amalloy | ,(partition 3 1 (range 5)) |
| 17:31 | clojurebot | ((0 1 2) (1 2 3) (2 3 4)) |
| 17:33 | crazydiamond | ah... yep |
| 17:33 | crazydiamond | I was thinking it returns only first one |
| 17:33 | crazydiamond | lol |
| 17:34 | crazydiamond | thanks for help! |
| 17:37 | amalloy | anyway, the simpler solution i alluded to earlier is ##((fn all-subvecs [xs] (cons [] (mapcat #(partition % 1 xs) (range 1 (inc (count xs)))))) (range 4)) |
| 17:37 | lazybot | ⇒ ([] (0) (1) (2) (3) (0 1) (1 2) (2 3) (0 1 2) (1 2 3) (0 1 2 3)) |
| 17:43 | crazydiamond | I'm so far from real solution. 'Cause what I want to do in the end is to extract common parts from functions (automated refactoring). And now I'm considering turning 1-argument function chains like (a (b (c))) and (a (b (d))) into [[(fn f0001 [x] (a (b x)))] (f0001 (c)) (f0001 (d))] |
| 17:50 | joefromct | does anyone use parkour? I'm getting a strange class-not-found exception from the stable github checkout, and it's as simple as adding a lein dep.... no idea if i'm missing something. doesn't seem to be any issues. |
| 17:50 | joefromct | (from others) |
| 17:53 | hiredman | I haven't used it in a while, what class not found exception? |
| 17:55 | hiredman | I think I had to add a few hadoop artifacts as provided dependencies when I used it |
| 17:55 | joefromct | hiredman: yeah, i think thats what i did wrong. i bet i added hadoop stuff, but incorrect versions for my cluster or something . |
| 17:56 | hiredman | https://github.com/damballa/parkour/blob/master/doc/intro.md |
| 17:56 | hiredman | could be |
| 17:56 | joefromct | yeah, i copied those dependencies verbatum |
| 17:56 | joefromct | *verbatim |
| 17:56 | hiredman | ah, and if you are on emr, it needs to be 2.2.0 |
| 17:56 | hiredman | (or it did last time I was) |
| 18:01 | joefromct | hiredman: maybe it has something to do with my lein :user profile... i don't think it's pulling any of them. i just added hadoop as a project dep. and it pulled a bunch of dependencies |
| 18:01 | joefromct | weird |
| 18:01 | joefromct | *wierd |
| 18:01 | hiredman | yeah it shouldn't pull them in |
| 18:02 | hiredman | that is what provided means, it means whatever environment the project runs in will provide them |
| 18:02 | joefromct | so, if it's in my user profile, and i put ":provided" , i need to add to my classpath manually or something ? |
| 18:02 | hiredman | I don't think so |
| 18:02 | joefromct | i guess i need to read up on that |
| 18:02 | joefromct | i haven't used :provided for anything i've done this far |
| 18:03 | amalloy | i don't think you want a provided dependency in your user profile. that sounds weird |
| 18:03 | hiredman | the parkour local running should take care of it for testing, and the cluster will provide them for deploying |
| 18:03 | joefromct | ok. i'll just have to somehow be sure that my cluster "provides" that stuff at the same version and whatnot i guess. |
| 18:18 | Guthur | Hi, I was wondering if anyone has used Javelin with Reagent |
| 18:19 | Guthur | I like javelins cell approach for data flow, but find myself having to wrap the value in Ratoms to get the dynamic updates of reagent |
| 18:19 | Guthur | has anyone found a nice pattern for this |
| 18:32 | michaniskin | Guthur: you can check out https://github.com/HostelRocket/aspis for ideas |
| 18:34 | michaniskin | oh nevermind, i thought it was a different thing |
| 18:34 | jonathanj | is there something like (update) or (update-in) that operates on vectors? |
| 18:35 | hiredman | ,(update-in [0 1 2 3] [0] inc) |
| 18:35 | clojurebot | [1 1 2 3] |
| 18:35 | jonathanj | hrm, and if i have nested vecs? [["a" 0] ...] |
| 18:35 | hiredman | ,(update-in [0 [0 0] 2 3] [1 0] inc) |
| 18:35 | clojurebot | [0 [1 0] 2 3] |
| 18:37 | jonathanj | ,(map #(update-in % [1] inc) [["a" 0] ["b" 1]]) |
| 18:37 | clojurebot | (["a" 1] ["b" 2]) |
| 18:37 | jonathanj | but i was hoping for something more elegant |
| 18:38 | hiredman | you asked for something like update-in, and you got update-in exactly |
| 18:39 | jonathanj | no need to be defensive, i'm just trying to write better code |
| 18:40 | justin_smith | jonathanj: so what thing would be "like update-in but for vectors" and more elegant than using update-in with a vector? |
| 18:40 | jonathanj | i meant more elegant than map with an anonymous function |
| 18:40 | amalloy | jonathanj: ##(doc for) |
| 18:40 | lazybot | ⇒ "Macro ([seq-exprs body-expr]); List comprehension. Takes a vector of one or more binding-form/collection-expr pairs, each followed by zero or more modifiers, and yields a lazy sequence of evaluations of expr. Collections are iterated in a nested fashion, rightmost ... https://www.refheap.com/107101 |
| 18:41 | amalloy | is usually prettier than map with a lambda |
| 18:42 | TEttinger | ,(mapv (juxt first (comp inc second)) [["a" 0] ["b" 1]]) |
| 18:42 | clojurebot | [["a" 1] ["b" 2]] |
| 18:42 | hiredman | I am saying, you asked for update-in, and you got it, udpate-in works on maps and vectors the same way, if you wanted something more, then you should ask for that |
| 18:42 | TEttinger | no anonymous functions, tricky as heck |
| 18:43 | hiredman | it sounds like want you want is more like an a tree transform library like xlt or whatever for xml |
| 18:43 | TEttinger | anonymous functions can be more elegant that point free style when it's abused like I did |
| 18:43 | hiredman | I am sure someone has written such a thing for clojure, but writing your own shouldn't be that hard either |
| 18:44 | TEttinger | clojure.zip might be a good starting point? |
| 18:44 | jonathanj | i guess i want something like lenses |
| 18:50 | TEttinger | jonathanj: there's a series of articles on this blog about lenses in clojure, the first one is mostly about how update-in and assoc-in largely fill the same role. http://blog.podsnap.com/vanhole.html |
| 18:50 | jonathanj | TEttinger: yes, thank you, i found those upon searching for lenses in clojure, i'm busy reading through the series right now |
| 19:13 | bjarn | does anyone know if there is a way so pass a string directly into a query using java.jdbc’s query function, such that the string becomes a *part* of the query itself and not a parameter *to* the query? |
| 19:14 | hiredman | use the str function |
| 19:15 | hiredman | [(str "select * from " table-name " where foo = ?") 5] |
| 19:17 | bjarn | wow, that should have been way more obvious than it was five minutes ago |
| 19:18 | bjarn | wait, you mean the standard library will help with that??? |
| 19:18 | lazybot | bjarn: How could that be wrong? |
| 19:18 | bjarn | thanks! |
| 19:50 | shayne_ | Can you reference a let defined var from within a fn passed as an argument to the righthandside of that let declaration? |
| 19:51 | spieden_ | shayne_: i'm pretty sure the function will close over it. you can reference symbols defined within the same let in any case |
| 19:52 | shayne_ | e.g. (let [xyz (foo (fn [] (... xyz)))]) |
| 19:52 | hiredman | no |
| 19:53 | hiredman | the simple way to refer to that is as a recursive let binding |
| 19:54 | hiredman | the way to reason about it is ((fn [xyz] ....) (foo (fn [] (... xyz)))) |
| 19:55 | shayne_ | hiredman - new to this, mind elaborating a little? |
| 19:55 | hiredman | (which is not to say the forms are equivilant, but it correctly guides the intuition in this case) |
| 19:55 | shayne_ | thanks chewing on that |
| 19:57 | hiredman | some other languages have a thing called let rec, which will let you do stuff like that, clojure has letfn, which allows you to have local functions that are mutually recursive, but it is limited |
| 20:00 | shayne_ | hireman: a little more color on what I'm trying to do: (let [id (addListener obj (fn [] (...) (...) (removeListener obj id)))]) |
| 20:01 | shayne_ | the api I am working with emits an ID when you call addListener and I'd like to reference that ID within this closure. Something that's fairly trivial in, say, JavaScript. |
| 20:02 | shayne_ | hiredman* ^^ |
| 20:12 | futuro | it sounds like what you want to do is bind a new value to id based on a previous value in id, so something like (defn fun [id] (let [id (addListener ...)])) |
| 20:16 | shayne_ | furturo: close, but I actually do want the most recent reference to remove the listener's callback from its own callback |
| 20:18 | futuro | It doesn't? |
| 20:20 | shayne_ | the value of id is the listener that was just added and inside of the listener's callback I want to remove the listener so the callback (itself) will no longer be called.... in js: var id = SomeObj.addListener(()=> { ...; SomeObj.removeListener(id)}) |
| 20:23 | futuro | everything I've seen so far seems to imply that precisely what you have described should happen, and then id will be bound to whatever value is returned by addListener, so I have no clue why it wouldn't |
| 20:30 | shayne_ | futuro: given the let method I described or another suggestion? |
| 20:31 | futuro | given the let method you described |
| 20:33 | shayne_ | futuro: it doesn't appear to (it's nil) as the let expression is evaluated the fn that acts as the callback is not capturing the named var on the left-hand side of the let. |
| 20:36 | futuro | you'd need to pass in id before hand. That is, inside the let expression the first id will have no value initially. So (let [foo (some-fn) bar (str foo)]), before some-fn is evaluated, is somewhat like (let [nil (some-fn) bar nil]), since none of the LHS variables have had values assigned to them yet |
| 20:38 | futuro | if you want to pass in a value for id to some-fn at a later time you could use an anonymous function, or (partial), but since id hasn't had a value assigned to it yet, referencing it is nonsensical |
| 20:39 | shayne_ | with: (let [id 0 id (addListener obj (fn [] (...) (removeListener obj id)))]) id: is always 0 |
| 20:42 | futuro | does addListener return a value? |
| 20:42 | shayne_ | futuro: yes |
| 20:42 | justin_smith | futuro: that wouldn't change how id is assigned - the initial value will always be shadowed |
| 20:42 | shayne_ | futuro: Here's my workaround: (def id 0) (set! id (addListener obj (fn [] (...) (removeListener obj id)))) |
| 20:42 | justin_smith | though the addListener call will not be able to use it's own return value in that lambda |
| 20:43 | justin_smith | that's terrible - def should not be used that way |
| 20:44 | shayne_ | justin_smith- just a workaround |
| 20:44 | shayne_ | and I know it's terrible... bleck |
| 20:44 | futuro | justin_smith: id is not shadowed before the binding inside of let, but every reference afterwards it will be |
| 20:44 | shayne_ | I really felt like this was something I was overlooking, because this pattern is so simple in other languages. |
| 20:45 | futuro | ,((fn [x] (let [x (inc x)] x)) 1) |
| 20:45 | clojurebot | 2 |
| 20:46 | futuro | shayne_: without knowing more about what code you're working with, it sounds like you might be running into trouble with the immutable data structures of clojure, and the various methods of forcing mutability (like swap!) |
| 20:46 | shayne_ | the problem is the lambda is passed into the expression that returns the value it needs to capture |
| 20:46 | shayne_ | again consider the JS... var id = obj.addListener(function() { ...; obj.removeListener(id); }); |
| 20:47 | justin_smith | futuro: shayne_: the answer is to use a promise |
| 20:47 | shayne_ | there's no immutability |
| 20:47 | shayne_ | err... mutation |
| 20:47 | shayne_ | justin_smith: right |
| 20:47 | shayne_ | justin_smith: doing that now |
| 20:47 | justin_smith | (let [id (promise)] (deliver id (fn [] ... (something @id)))) |
| 20:47 | justin_smith | that's how you do it |
| 20:48 | justin_smith | shayne_: you understand the problem with using def right? def only creates globals, it doesn't do local scope |
| 20:49 | justin_smith | err, by globals I mean vars at the top level of a namespace, of course |
| 20:50 | shayne_ | justin_smith: was just playing with the code, but a good reminder |
| 20:51 | justin_smith | shayne_: I'm pedantic about that one because it is one of the biggest differences between clojure and scheme, and a common mistake of newcomers from scheme |
| 22:00 | arrdem | Bronsa: ping |
| 22:03 | Bronsa | arrdem: pong |
| 22:05 | namra | does somenone know a vim command to move n lines up or down? |
| 22:06 | namra | the motion docs don't provide any info |
| 22:06 | justin_smith | namra: <x>t<y> where x and y are numbers |
| 22:07 | justin_smith | namra: never mind, that is wrong |
| 22:07 | namra | justin_smith: sorry didn't state my question clearly, i just want to move the cursor |
| 22:08 | justin_smith | namra: oh, <x>j or <x>k to move x lines down or up |
| 22:08 | justin_smith | there is also Control+f and control+b for moving by screens |
| 22:09 | namra | justin_smith: yes those are the key commands. but they don't work as commands. |
| 22:09 | justin_smith | oh, I misunderstood |
| 22:09 | arrdem | Bronsa: got a minute to look at the ns-publics TANAL tree with me? trying to understand what the desired output bytecode is. |
| 22:10 | Bronsa | arrdem: yeah sure |
| 22:11 | arrdem | Bronsa: https://www.refheap.com/107106 trimmed tree of only the relevant lambda |
| 22:12 | Bronsa | arrdem: are you trying to understand why temjvm explodes on ns-publics? |
| 22:13 | arrdem | Bronsa: da |
| 22:13 | arrdem | and fix... |
| 22:13 | Bronsa | arrdem: I thought I already mentioned the reason |
| 22:13 | Bronsa | arrdem: it's not an analysis bug |
| 22:13 | arrdem | Bronsa: maybe last year I didn't find it in my notes. |
| 22:14 | Bronsa | arrdem: no i mean, I remember mentioning it like 3 days ago in slack I might have not highlighted you though |
| 22:14 | arrdem | Bronsa: yeah I don't remember that |
| 22:15 | Bronsa | arrdem: look at the source code for ns-publics -- it's something like (fn [^Var v] (if (instance? Var v) (.something v))) |
| 22:15 | arrdem | Bronsa: yep |
| 22:15 | arrdem | Bronsa: I get what's going on here, my real question is "is there something silly we could/should do so this just works" |
| 22:16 | Bronsa | arrdem: the only way to fix this is to change the emitter to emit "untyped" locals like Compiler.java does so that type hints are not enforced |
| 22:17 | arrdem | Bronsa: gotcha. OK. |
| 22:17 | Bronsa | arrdem: can't estimate how much work that'll be. might be tricky |
| 22:18 | arrdem | Bronsa: but for that it'd get us another step towards CinC I can't honestly say it's a feature |
| 22:19 | arrdem | or rather but that |
| 22:19 | Bronsa | err, what? |
| 22:20 | arrdem | there is no point to that change but to make loading core work |
| 22:20 | Bronsa | yeah |
| 22:20 | arrdem | tempted to special case the eval of that AST to eval a less silly AST... |
| 22:21 | Bronsa | arrdem: I mean, clojure.org explicitely states that type hints are not enforced so I take the blame for this |
| 22:22 | arrdem | Bronsa: yeah. OK. I'll take a look at this and see what I can do while remaining a Clojure compiler. |
| 22:22 | Bronsa | (I still think that there's no good reason not to enforce type hints) |
| 22:22 | namra | justin_smith: one could specify the absolute line number. i.e. in command mode just type the line number and the cursor will jump to it |
| 22:22 | arrdem | you and me both |
| 22:23 | arrdem | but if we ever figure out how to do emitter configs that's something a user could toggle |
| 22:23 | Bronsa | yes |
| 22:23 | arrdem | s/emitter configs/modular emitters/g |
| 22:24 | arrdem | any thoughts on that? I threw some stuff at the wall but nothing I thought would really be performant. |
| 22:24 | arrdem | kinda tempted to prioritize usability over perf |
| 22:24 | Bronsa | this particular use case could even be done as an AST rewrite without touching the emitter I think |
| 22:24 | Bronsa | rewrite pass* |
| 22:24 | arrdem | I mean you just drop the local tags or retag to Object |
| 22:25 | arrdem | which we could do by default... |
| 22:25 | Bronsa | yeah, and move the cast to the interop point |
| 22:26 | arrdem | right that's what I was going to try. don't do any casting or typing until you interop and then checkcast. |
| 22:26 | Bronsa | that's what compiler.java does, yeah |
| 22:26 | arrdem | simplest stupidest thing that could possibly work... |
| 22:26 | Bronsa | has the disadvantage of requiring a checkcast for every interop form |
| 22:27 | arrdem | so one thing I've been thinking about is that the JVM bytecode we emit really hides the stack as a dataflow graph |
| 22:27 | arrdem | don't know how to do this yet, but given a dataflow representation of generated code fixing stuff like that is dead easy |
| 22:27 | Bronsa | arrdem: depending on how bored I get next week I *might* try and refactor/rewrite the casting/boxing mess in t.a.jvm |
| 22:28 | arrdem | Bronsa: please do, it'll probably improve my test coverage as well <3 |
| 22:28 | arrdem | anyway I'm gonna keep banging on cleaning up the emitter for fun |
| 22:29 | arrdem | hopefully I'll get to try profiling the new flatten stuff soon but I'd like to clear the current bug list before potentially introducing more with that |
| 22:30 | arrdem | so if there are others I should know about speak now :P |
| 22:31 | TEttinger | yaaaay bug fixes |
| 22:31 | Bronsa | arrdem: other than the reflector stuff in the analyzer, I'm not aware of any other |
| 22:33 | Bronsa | arrdem: btw re the emitter, it would be interesting to know how much time is spent building up the bytecode data structure vs how much time it's spent traversing it & emitting actual bytecode |
| 22:34 | arrdem | Bronsa: I seem to recall from your previous investigations that we're wasting a _lot_ of time in concat right now. |
| 22:34 | arrdem | Bronsa: hence the flatten stuff that does a single pass loop unrolled conj! with no intermediary datastructures |
| 22:34 | Bronsa | yeah, that definitely dominates time |
| 22:35 | TEttinger | uh, is this the flatten that is |
| 22:35 | TEttinger | ~flatten |
| 22:35 | clojurebot | flatten is rarely the right answer. Suppose you need to use a list as your "base type", for example. Usually you only want to flatten a single level, and in that case you're better off with concat. Or, better still, use mapcat to produce a sequence that's shaped right to begin with. |
| 22:35 | arrdem | TEttinger: no this is my own flatten impl that is more domain specifi |
| 22:35 | arrdem | *c |
| 22:35 | TEttinger | cool |
| 22:35 | TEttinger | I wouldn't bother to optimize clojure.core/flatten |
| 22:36 | arrdem | TEttinger: https://github.com/clojure/tools.emitter.jvm/blob/054873c0f1e553c9941b4675b31b42d8c1eed654/src/main/clojure/clojure/tools/emitter/jvm/emit.clj#L66 |
| 22:36 | Bronsa | but if the time spent traversing/transforming is still significant it might mean that we need to rethink the emission thing alltoghether -- maybe the current representation is just too expensive? |
| 22:36 | arrdem | yeah who knows. only way to find out is to benchmark that thing. |
| 22:37 | arrdem | which involves rewriting half the bloody emitter to avoid `~@ concats |
| 22:37 | TEttinger | unicode! who put unicode in this code! (λ AST → Options) → Bytecode |
| 22:37 | Bronsa | yup |
| 22:37 | arrdem | that would be me |
| 22:37 | arrdem | like.. last yeart |
| 22:37 | arrdem | *year |
| 22:37 | TEttinger | arrdem, but that's Scala's turf |
| 22:38 | TEttinger | you'll start a gang war |
| 22:38 | arrdem | TEttinger: FITE ME U NERD |
| 22:38 | arrdem | I like my type signature docstrings thnx |
| 22:38 | TEttinger | http://scalaz.github.io/scalaz/scalaz-2.9.0-1-6.0.1/doc.sxr/scalaz/Kleisli.scala.html |
| 22:38 | TEttinger | I have that bookmarked under IDEAL SCALA CODE |
| 22:39 | TEttinger | intuitive and clear. |
| 22:39 | TEttinger | def <=<[C](k: C => M[A])(implicit b: Bind[M]): Kleisli[M, C, B] = ☆(k) >=> this |
| 22:39 | TEttinger | it hurts me. |
| 22:40 | arrdem | Bronsa: you still using those UTF8 rewrite rules you gave me last summer? |
| 22:41 | arrdem | Bronsa: and yeah I don't know what we'll have to do if this representation is just too slow. |
| 22:41 | arrdem | I want to play with building a data flow model for JVM bytecode that we can use as yet another intermediary target for writing a real peephole optimizer, but that's a ways out. |
| 22:44 | Bronsa | arrdem: haven't changed my emeacs setup in years |
| 22:44 | Bronsa | i know those rewrite rules mess with how my code is indented but I kinda sorta don't really care :P |
| 22:45 | arrdem | Bronsa: this typifies your code style yes :P |
| 22:46 | Bronsa | arrdem: yeah. the current representation doesn't really buy us that much over directly emitting bytecode |
| 22:47 | arrdem | Bronsa: I mean... I love TEMJVM just for the existing representation because it's a friggin clojure datastructure I can muck with and understand, but yeah we could build something good for a lot more leverage. |
| 22:47 | arrdem | Bronsa: I think the unchecked locals patch is gonna be dead easy, just a sec |
| 22:48 | Bronsa | it was definitely invaluable when writing it. Couldn't imagine debugging emission bugs without the data representation |
| 22:49 | Bronsa | but i'm not too sure it's the best approach. doesn't buy us enough benefits to justify the performance hit |
| 22:49 | arrdem | I would have given up on Oxcart but for it... |
| 22:49 | Bronsa | dunno. I'd like to be happy with the analyzer infrastructure before investing time refactoring/rewriting the emitter |
| 22:49 | arrdem | Bronsa: I mean I think anything else we do would be a sources as leafs dataflow graph |
| 22:50 | arrdem | so you have a tree of ops which have nth things on the stack which will be popped as children |
| 22:51 | arrdem | your sources being constants and ILOADs |
| 22:51 | arrdem | and your sinks being pops whether implicit or explicit |
| 22:52 | arrdem | this would let you consider ops like checkcast to be pop 1 push 1 adding metadata, and now you can walk the dataflow path backwards to eliminate duplicate casts. |
| 22:52 | arrdem | ^ this being the reason that I'm messing with any of this again actually |
| 22:53 | Bronsa | right, and stuff like intrinsics could be implemented in a less hackish way than it is now |
| 22:57 | arrdem | interestingly the flatten stuff gets us part of the way there... |
| 22:58 | arrdem | if you squint really hard at the "can force thunks of 0 arguments" case |
| 23:02 | Bronsa | going to sleep now |
| 23:03 | arrdem | g'night thanks as always |
| 23:05 | mtamrf | what's an idiomatic way to express a nested for loop with side effects (dosync)? |
| 23:13 | arrdem | mtamrf: can doseq not do what you need? |
| 23:13 | arrdem | mtamrf: note that doseq can go over multiple sequences and supports all of for's sequence expressions |
| 23:14 | justin_smith | mtamrf: dosync is for refs, you are likely thinking of doseq? or do you want a doseq of dosyncs? that almost sounds like a doctor seuss poem |
| 23:14 | justin_smith | (doc dosync) |
| 23:14 | clojurebot | "([& exprs]); Runs the exprs (in an implicit do) in a transaction that encompasses exprs and any nested calls. Starts a transaction if none is already running on this thread. Any uncaught exception will abort the transaction and flow out of dosync. The exprs may be run more than once, but any effects on Refs will be atomic." |
| 23:18 | mtamrf | typo - I did mean doseq |
| 23:22 | arrdem | aaand bronsa no longer idles on a proxy ok |
| 23:23 | arrdem | whelp I think I found a two local fix to that issue rather than some huge rewrite.. |
| 23:33 | TEttinger | nice arrdem |
| 23:39 | gganley | are there any meetups in MA that is should know about? |
| 23:42 | arrdem | aaaand Stackmap issues |
| 23:42 | arrdem | fml |
| 23:43 | arrdem | TEttinger: yeah turns out not checking casts is pretty easy :P |