2015-02-21
| 01:56 | jux | hi |
| 01:58 | jux | I got strange behavior from clojure.java.jdbc. when I do query (sql/query dsn ["select id, name from ns.users where id > ? order by id" 1]) |
| 01:59 | jux | all strings are returned surrounded by \" |
| 01:59 | jux | so I get ({:id 1 :name "\"John Doe\"}) |
| 02:00 | jux | what can be the reason? |
| 02:03 | hiredman | you are double printing, either on the way out or the way in |
| 02:20 | jux | hiredman: these things show in my unit tests, so it's not a thing of printing |
| 02:20 | jux | and I inserted data directly using Navicat |
| 02:32 | jux | hell, you were right |
| 02:47 | ewemoa | alexyakushev: having trouble building foreclojure-android -- is it supposed to build atm? |
| 02:48 | alexyakushev | ewemoa: What error do you get? |
| 02:52 | ewemoa | alexyakushev: https://pastee.org/68hs4 |
| 02:53 | alexyakushev | ewemoa: Let's take it to #clojure-android, shall we? |
| 03:51 | Eremox | Is there some thing like scala's Akka in clojure? |
| 03:52 | irctc__ | Hi everybody. Is there a shorter version to do this multi arity-function? because for both type of arguments, the same function is called: (defn get-15min-range ([{start :start-time end :end-time}] (range start (+ end 900) 900)) ([start end] (range start (+ end 900) 900))) |
| 03:59 | amalloy | irctc__: you can make the first arity call the second one |
| 04:03 | irctc__ | great, i will to this! I have a second question. when i have two destructuring arguments in a multi-arity function [{a :a b :b}] and [[a b]] - i get a warning that both arguments are the same length, even if one is a map and the other one is a vector. How can i express this destructuring in an multiple-arity function? |
| 04:32 | amalloy | you can't. clojure's functions are only overloaded on argument count, not on anything else. if you want different bodies based on something else, oyu need to do more than write a multi-arity function. for example, write a multimethod instead |
| 04:47 | the-kenny | Or you can use core.match if you want the 'matching' style. |
| 04:48 | irctc__ | i will look into core.match and multimethods - thanks for the advice |
| 05:49 | nw2 | probably a stupid question, but in this example https://www.refheap.com/97610, the call to the "foo" macro throws a ClassNotFoundException, but the "bar" macro works fine. is there any way to make the "foo" macro work? |
| 05:51 | Bronsa | nw2: you want & body not body in the argvec |
| 05:52 | hyPiRion | (macroexpand-1 '(foo (File. "/tmp"))) => (do File. "/tmp") |
| 05:53 | nw2 | Bronsa: ah, of course! thank you very much |
| 06:19 | Guthur` | Hi, I'm just starting with Clojure usign Emacs and CIDER |
| 06:19 | Guthur` | I was wondering if there is an eldoc style hint system with CIDER |
| 06:20 | Guthur` | so that I can get information about the interface for a function etc |
| 06:20 | Eremox | Why is sicp recommended? |
| 06:21 | AimHere | Partly because it introduces a lot of important programming concepts. Also it uses a lisp as it's introductory programming language, which turns some nerds on. |
| 06:22 | darrenh | I have a question about the component framework. If I want to vary a protocol implementation based on configuration, is there a good way to go about it? My use case is where I want to wrap one of my components with one that intercepts the calls based on a configuration flag. |
| 06:22 | Eremox | Which would be better to read first sicp or ctmcp |
| 06:42 | tomjack | Guthur`: yes? it doesn't work for you? |
| 06:42 | tomjack | oh, it looks like I had to do (require 'cider-eldoc) (add-hook 'cider-mode-hook (lambda () (cider-turn-on-eldoc-mode))) |
| 06:48 | Guthur` | tomjack: ah, ok I will give that ago |
| 06:53 | Guthur` | tomjack: excellent,thanks |
| 06:54 | tomjack | if you use paredit, I'm not sure it works right |
| 06:54 | tomjack | http://emacswiki.org/emacs/ParEdit#toc2 |
| 06:55 | tomjack | but it seems kind of nuts that only those two commands are on the list! |
| 07:01 | Guthur` | Yeah i normally use paredit, but have not added it to a clojure mode hook yet |
| 08:17 | jaen | Is there any way to dump leiningen middleware stack and/or hooks so I know where certain behaviour comes from (or a verbose execution mode that says so)? |
| 08:44 | the-kenny | jaen: with lein pprint, yes. |
| 08:44 | the-kenny | jaen: https://github.com/technomancy/leiningen/tree/master/lein-pprint |
| 08:51 | jaen | the-kenny: thanks. It shows no middleware however. That's weird o_0 |
| 09:13 | doright | Clojure appears in only 17 London job titles on www.indeed.co.uk compared with Scala:386 and Groovy:70. What is required to increase Clojure's adoption? |
| 09:13 | pandeiro | what would be the way to walk a sequence of nested sexps and produce a sequence of all of them, eg: (f '((foo (bar baz)))) => ((foo (bar baz)) (bar baz)) |
| 09:15 | the-kenny | pandeiro: check clojure.walk |
| 09:16 | the-kenny | doright: My guess is recruiters write scala on their job offers because it's sooooo enterprise. |
| 09:16 | doright | 10 of these jobs include other languages in the title so 7 are exclusively Clojure, cf. Scala:297 and Groovy:42 |
| 09:16 | pandeiro | the-kenny: yeah i don't see how to accumulate the result w/ walk |
| 09:16 | pandeiro | the-kenny: aside from using them for side effects w/ an atom |
| 09:18 | the-kenny | pandeiro: hm. clojure.core/tree-seq then maybe? |
| 09:19 | the-kenny | ,(rest (tree-seq sequential? seq '((foo (bar baz))))) |
| 09:19 | clojurebot | ((foo (bar baz)) foo (bar baz) bar baz) |
| 09:21 | the-kenny | now throw away non-lists and bingo |
| 09:21 | pandeiro | the-kenny: nice, thank you |
| 09:22 | irctc__ | Hi everybody. How can i union a seq/list of sets? (union '(#{1}) #{1}) |
| 09:22 | irctc__ | sorry... like this one (union '(#{1} #{1}) |
| 09:22 | the-kenny | ,(apply union [#{42 23} {1 2 3 42}]) |
| 09:22 | clojurebot | #<CompilerException java.lang.RuntimeException: Unable to resolve symbol: union in this context, compiling:(NO_SOURCE_PATH:0:0)> |
| 09:22 | the-kenny | ,(apply clojure.set/union [#{42 23} {1 2 3 42}]) |
| 09:22 | clojurebot | #<CompilerException java.lang.ClassNotFoundException: clojure.set, compiling:(NO_SOURCE_PATH:0:0)> |
| 09:23 | the-kenny | damn you clojurebot |
| 09:23 | AimHere | ,(apply clojure.set/union [#{42 23} #{1 2 3 42}]) |
| 09:23 | clojurebot | #<CompilerException java.lang.ClassNotFoundException: clojure.set, compiling:(NO_SOURCE_PATH:0:0)> |
| 09:23 | irctc__ | great! thanks! |
| 09:23 | the-kenny | irctc__: either apply or reduce, a matter of taste. I prefer reduce when the size of the input list is unknown. |
| 09:25 | the-kenny | AimHere: whoops, just noticed the missing # in my code. |
| 09:26 | AimHere | Didn't do much good, as far as clojurebot was concerned |
| 09:27 | the-kenny | yeah, no difference here. |
| 09:29 | vas | ,(apply hash-map [:a 5 :b 6]) |
| 09:29 | clojurebot | {:b 6, :a 5} |
| 09:29 | vas | ,(reduce hash-map [:a 5 :b 6]) |
| 09:29 | clojurebot | {{{:a 5} :b} 6} |
| 09:29 | the-kenny | it doesn't work for all functions, that's obvious |
| 09:52 | hyPiRion | If you have a function on the form (fn f ([a] (f a init-val)) ([a b] ...)), is it more idiomatic to put the `b` arg at the end or in the front? |
| 09:52 | hyPiRion | Assuming that people are unlikely to use the two-ary version on a daily basis |
| 09:56 | seancorfield | hyPiRion: I think I would expect ((f x) y) == (f x y) so b should be at the end... But I know there are sometimes good reasons for not doing that (I think there are core fns like that?). |
| 09:59 | hyPiRion | seancorfield: yeah, the sort-functions in core is probably doing that to keep the coll at the end like map and friend does |
| 09:59 | hyPiRion | It won't be used in that setting though, so I guess it makes more sense to just put it at the end |
| 10:02 | seancorfield | Yeah, the "coll arg last" idiom is a good reason for ((f x) y) to be (f y x) :) |
| 10:16 | ambrosebs | how do I tell lein which project.clj to use without cd'ing into it? |
| 10:16 | ambrosebs | I want to run the tests of some subfolder in a Makefile |
| 10:18 | Glenjamin | sh -c "cd subfolder && lein test" ? |
| 10:18 | Glenjamin | not really what you asked, but should work |
| 10:20 | ambrosebs | Glenjamin: thanks :) |
| 11:02 | zling | earnestly |
| 11:14 | justin_smith | doright: maybe not be a lisp? |
| 11:15 | justin_smith | ambrosebs: you can use cd in make commands |
| 11:15 | justin_smith | I typically use a pushd / popd combo though |
| 12:14 | Guest40845 | Hey there, off topic question but can I PM someone who has experience leaving a software project written by a very small team or you're the only guy on the team? Trying to make a career move and I don't want to hurt the company I'm with… |
| 12:33 | justin_smith | Guest40845: I've done a handoff before. Leave detailed comments describing what each file is for, and add an overview tying all of them together in the README. Also, be sure that the README has a description of how to build, run, deploy that can be followed by the letter without any implicit steps |
| 12:41 | Guest40845 | justin_smith: mind if i pm? |
| 12:43 | justin_smith | go ahead, sure |
| 13:02 | dnolen | ClojureScript 0.0-2913 released, Google Closure Modules, and better underlying architecture for supporting nREPL https://groups.google.com/d/msg/clojurescript/n_8WHnlcOGI/1kmATGABVi0J |
| 14:19 | underplank | Hi all im trying to instansiate a java ProcessBuilder class like so |
| 14:19 | underplank | orochi.core.api=> (ProcessBuilder. 'python') |
| 14:19 | underplank | IllegalArgumentException No matching ctor found for class java.lang.ProcessBuilder clojure.lang.Reflector.invokeConstructor (Reflector.java:183) |
| 14:19 | underplank | Any idea why? |
| 14:21 | underplank | apparently it doesnt have a constructor? or something? |
| 14:25 | i-blis | underplank: try (ProcessBuilder. (list "python")) |
| 14:25 | kriyative | @underplank -- did you mean "python" with double-quotes? |
| 14:25 | underplank | I meant a string of “python” |
| 14:25 | i-blis | underplank: the constructor takes a list |
| 14:26 | underplank | i-blis: huh. it also looks like it take a string. but, thats cool. I could be looking at an old api |
| 14:26 | underplank | yup. using a list works. thanks! |
| 14:27 | i-blis | :) |
| 14:27 | underplank | yeah I should look at the version of the docs I am looking at. Apparently back in Java 1.5 you could ue a string. |
| 14:27 | underplank | :) |
| 14:28 | underplank | thanks i-blis ! |
| 14:31 | csd_ | Are any of you running CIDER 0.9.0-snapshot? |
| 14:32 | i-blis | you're welcome underplank |
| 14:34 | i-blis | csd_: went back to 0.8.2 a week or so ago : did jack in but did not eval code |
| 14:34 | csd_ | i-blis: i can't jack in.. gives me a class path error |
| 14:35 | i-blis | csd_: use stable :) |
| 14:35 | csd_ | yeah :-/ |
| 14:35 | justin_smith | underplank: ProcessBuilder takes either varargs string, or list of string. It turns out that via clojure you need to provide either a list, or a String-array because varargs are a java compiler thing and don't exist on a vm level |
| 14:36 | i-blis | csd_: or do you absolutely need boot support? |
| 14:36 | underplank | ahhh, right. that makes sense. So when they say “String” in the constructor, thats not actually what that means :) |
| 14:36 | csd_ | i had been using the snapshot for a while because of a bug i'd run into a while back |
| 14:37 | csd_ | its just sort of annoying because i get packages from the "non-stable" branch of MELPA but this is the one thing ill have to have to get from the stable branch |
| 14:37 | i-blis | 0.8.2 seems pretty stable |
| 14:38 | i-blis | csd_: you can pin the package |
| 14:38 | vas | so I end up with some enlive vectors that look like: [{nodes}] and [{nodes}] .... how do you combine two vectors? (am i right in calling them vectors because they're in square brackets?) |
| 14:38 | csd_ | i-blis: awesome |
| 14:38 | vas | ( ideal result is just sequential [{nodes}, {nodes}] ) |
| 14:39 | kriyative | i-blis: how do you pin packages from MELPA? I switched to el-get just for that ability. |
| 14:39 | i-blis | csd_: (add-to-list 'package-pinned-packages '(cider . "melpa-stable") t) |
| 14:40 | kriyative | i-blis: good to know, thanks. |
| 14:41 | i-blis | csd_: provided you added melpa-stable to packages-archives, igws |
| 14:44 | vas | huzzah for concat. |
| 14:46 | csd_ | i-blis: looks like MELPA-stable has 0.7.0 latest |
| 14:48 | i-blis | csd_: are you sure you're using stable.melpa.org? |
| 14:48 | csd_ | positive |
| 14:48 | i-blis | http://stable.melpa.org/#/cider has 0.8.2 |
| 14:48 | csd_ | weird, thats not what Packages shows |
| 14:49 | i-blis | csd_: emacs 24? |
| 14:49 | csd_ | no, 25 |
| 14:49 | i-blis | well, shouldn't be the issue |
| 14:50 | csd_ | oh wait i think i have a syntax in my emacs config |
| 14:50 | csd_ | err a syntax error |
| 14:51 | i-blis | do you have : (add-to-list 'package-archives |
| 14:51 | i-blis | '("melpa-stable" . "http://stable.melpa.org/packages/") t) |
| 14:52 | csd_ | i-blis: there we go :) |
| 14:52 | csd_ | thank you |
| 14:53 | i-blis | csd_: you're welcome |
| 14:54 | csd_ | one of those times where you go to do some work and unexpectedly waste a half hour tooling :-/ |
| 14:56 | gfredericks | does anybody know how to throw this exception via normal clojure usage (i.e., without interopping in the Var class)? |
| 14:56 | gfredericks | https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Var.java#L218 |
| 15:05 | i-blis | gfredericks, why not: (throw (java.lang.IllegalStateException.)) |
| 15:12 | gfredericks | i-blis: I'm more asking about how to get to that line of code |
| 15:12 | gfredericks | I want to know if it's even possible |
| 15:15 | amalloy | gfredericks: (binding [x 1] (.start (Thread. #(set! x 2)))) maybe? |
| 15:17 | i-blis | gfredericks: I am not sure I understand, you want to throw an exception as if a var was set outside its thread? |
| 15:18 | gfredericks | amalloy: if it's not bound on the new thread then you'll just get a normal can't-set!-a-var-without-a-thread-local-binding |
| 15:18 | gfredericks | i-blis: yep; based on my understanding that line should be unreachable, trying to figure out if there's anything I'm missing |
| 15:27 | justin_smith | gfredericks: is it that you got that exception and want to know how it could have happened? |
| 15:27 | kryft | What would be the best way to ensure that a function can't be called if a call to that function is currently in progress? So basically a simple lock |
| 15:28 | justin_smith | kryft: wrap the function body in a call to locking? |
| 15:29 | justin_smith | ,(defn locked [x] (locking locked (inc x))) |
| 15:29 | clojurebot | #'sandbox/locked |
| 15:29 | justin_smith | it locks on itself |
| 15:29 | justin_smith | ,(locked 1) |
| 15:29 | clojurebot | 2 |
| 15:29 | kryft | justin_smith: Ok, that seemed to be the solution based on some quick googling, but it was usually accompanied by "you should rarely need to use locking" :) |
| 15:29 | kryft | justin_smith: So I thought I'd check that there isn't a more idiomatic way |
| 15:29 | justin_smith | kryft: well, why is it that the function needs to be locked? |
| 15:29 | justin_smith | the more idiomatic way is not to need locking at all :) |
| 15:29 | amalloy | kryft: well indeed you should rarely need to do this |
| 15:30 | justin_smith | kryft: usually we use immutable data structures so that locking is irrelevant |
| 15:30 | gfredericks | justin_smith: nope have never got it as far as I know |
| 15:30 | gfredericks | just wondering if it's dead code or not |
| 15:30 | justin_smith | gfredericks: ahh, I misunderstood then |
| 15:30 | justin_smith | OK |
| 15:31 | justin_smith | kryft: amalloy: of course when interacting with an external system we sometimes lose some of the tidiness that immutability offers, and locking is sometimes needed |
| 15:32 | justin_smith | kryft: but for example if your algorithm assumes locking and is 100% clojure, there is probably a nice immutable lock-free way to do it |
| 15:32 | kryft | justin_smith: Well, my app sends emails every now and then, and it keeps emails in a postgresql DB (with a lot of other data). The function in question fetches unsent emails and tries to send them. |
| 15:32 | justin_smith | and the locking is because you don't want emails sent more than once? |
| 15:32 | kryft | justin_smith: Right. |
| 15:32 | amalloy | kryft: well you have a whole database to work with! databases are great at lokcing |
| 15:33 | justin_smith | kryft: clojure doesn't have implicit paralellism - if you don't do the task from multiple threads or instances, you won't need to worry about that |
| 15:33 | amalloy | add a column "trying_to_send_right_now", and set that to true when you take it out, for example |
| 15:33 | amalloy | locking will only work inside one jvm, but if you put the lock in the database it will scale to any number of servers later on |
| 15:34 | justin_smith | that's a good point too, but I still think it's easier to just ensure that only one thread in one instance is email sending. It's not as if email sending is a bottleneck that you need to parallelize for performance in most cases. |
| 15:36 | kryft | justin_smith: Currently the sending could be triggered from multiple threads - basically I try to send as soon as I add a new email to the table. The table is there mostly to keep track of what's been sent and because it's naturally possible that sending could fail. |
| 15:37 | justin_smith | kryft: OK. My approach would be to have a single thread responsible for sending emails, perhaps offering a queue that other threads could put emailing tasks onto. |
| 15:38 | justin_smith | kryft: though perhaps there is some other factor that makes emailing from arbitrary contexts advantageous? |
| 15:39 | kryft | justin_smith: Not really - I just want to make sure they get sent relatively quickly (for example, a user registers and gets a confirmation immediately) |
| 15:39 | justin_smith | in general I find if something needs to be done exactly once, having exactly one thread in charge of it is helpful |
| 15:39 | kryft | justin_smith: So I'm sure a single thread would work. |
| 15:40 | justin_smith | you could also use core.async instead of a thread and a queue, if you find other places where core.async would be useful |
| 15:40 | justin_smith | anyway, this eliminates the need for locking logic, because that single thread of execution (or logical thread of execution in the core.async case) can reliably track what has and hasn't been done |
| 15:41 | justin_smith | since it is syncronous in its own context |
| 15:42 | kryft | justin_smith: Right, sounds good. It didn't occur to me to have an explicit thread for that. |
| 15:43 | kryft | amalloy: I considered using the DB for this, but I wasn't sure that it could be done without using DB locks (which clojure's jdbc interface doesn't seem to support) |
| 15:43 | justin_smith | another (mostly equivalent) option is to use an agent, and store the emailed addresses in the agents, sending it a function that sends an email if it hasn't been sent yet |
| 15:44 | justin_smith | s/agents/agent |
| 15:44 | kryft | amalloy: (I'm fairly new to postgresql too, so I may well be mistaken :) |
| 15:44 | kryft | It's my first backend, my first DB application, and my first clojure app! :P |
| 15:44 | justin_smith | best of luck! |
| 15:49 | kryft | justin_smith: Thanks! |
| 16:05 | kryft | justin_smith: For the "single responsible thread" approach, did you mean having a function regularly polling the queue and sending emails whenever there's something in there, or something fancier? |
| 16:05 | justin_smith | kryft: no need to poll even, just blocking read on the queue in a loop |
| 16:07 | kryft | justin_smith: Ah, right |
| 16:07 | justin_smith | kryft: so, if you use java.util.concurrent.BlockingQueue it would be a .take operation |
| 16:07 | justin_smith | (rather than .poll, which has a timeout) |
| 16:09 | justin_smith | and then .put for synchronous adding to the queue (but not synchronous waiting for it to be consumed) |
| 16:14 | kryft | justin_smith: Ah, nice (I was trying to find a structure like that in clojure) |
| 16:14 | justin_smith | kryft: there is clojure.lang.PersistentQueue |
| 16:15 | justin_smith | ,(let [q clojure.lang.PersietentQueue/EMPTY q' (conj q :a)] (into [] q')) |
| 16:15 | clojurebot | #<CompilerException java.lang.ClassNotFoundException: clojure.lang.PersietentQueue, compiling:(NO_SOURCE_PATH:0:0)> |
| 16:15 | justin_smith | oops |
| 16:15 | justin_smith | ,(let [q clojure.lang.PersistentQueue/EMPTY q' (conj q :a)] (into [] q')) |
| 16:15 | clojurebot | [:a] |
| 16:15 | justin_smith | but in that case you would want to put it inside an atom, ref, or agent |
| 16:16 | kryft | Right |
| 16:18 | kryft | justin_smith: Thanks again; I guess I'll just use a BlockingQueue here, but good to know that PersistentQueue exists |
| 16:19 | justin_smith | also, as I mentioned before, core.async is a mini language around channels, that are effectively queues |
| 16:19 | justin_smith | but it may be a bit much if you only need that kind of flow in one place |
| 16:24 | kryft | justin_smith: Yeah, that's probably overkill here; all other concurrency issues are basically handled by JDBC transactions. Also core.async seems to be in alpha at the moment? |
| 16:37 | justin_smith | kryft: alpha, but widely used |
| 16:38 | kryft | justin_smith: A bit like prismatic/schema then :P |
| 16:42 | justin_smith | I think it's wider deployed than schema though, I've had it in production a couple of instances myself |
| 16:42 | Graawr | hey ! is there a "pure clojure" (i.e. without wobbly java interop) way to extract a directory from the currently running jar ? I'm currently coming up with a direct translation of a java snippet using JarFIle & JarEntry, but it really feels wobbly |
| 16:43 | justin_smith | Graawr: in general, clojure core doesn't abstract very far from the host for IO stuff. That lib may exist out there somewhere though. |
| 16:44 | csd_ | can someone please look at this core.async snippet and tell me what i'm missing? i would expect 42 to be printed https://www.refheap.com/97625 |
| 16:48 | Graawr | justin_smith: okay, so I better keep going then. I'm translating this answer ( http://stackoverflow.com/questions/1529611/how-to-write-a-java-program-which-can-extract-a-jar-file-and-store-its-data-in-s/1529707#1529707 ) to clojure, although I'm lost at getting an input stream : (.getInputStream (jar-file jar-entry)) always returns nil |
| 16:50 | justin_smith | and jar-file is a java.util.jar.JarFile? |
| 16:50 | Graawr | yup |
| 16:50 | Graawr | and jar-entry is a java.util.jar.JarEntry |
| 16:50 | justin_smith | oh, the code there calls getInputStream on jar-file |
| 16:51 | justin_smith | not on the entry |
| 16:51 | justin_smith | err wait |
| 16:52 | Graawr | whoops, typo: (.getInputStream jar-file jar-entry) returns nil (there's no nesting) |
| 16:52 | justin_smith | so, I would expect this to work: (.getInputStream (java.util.jar.JarFile. "some/path/to.jar")) |
| 16:52 | justin_smith | why the extra arg? |
| 16:52 | justin_smith | the stream is the jar, not some entry in the jar |
| 16:52 | justin_smith | wait... |
| 16:53 | justin_smith | ok, so file is a java.io.File |
| 16:53 | justin_smith | you get an inputstream from the file |
| 16:54 | justin_smith | sorry, I'm all mixed up on this, I'm backing out for a moment |
| 16:54 | Graawr | oh okay, thank you ! |
| 17:00 | justin_smith | Graawr: my java is rusty - would the continue in that first if statement skip the rest of the body and start the next step of the while loop? |
| 17:01 | justin_smith | because if the first thing you got was a directory, I could see getInputStream returning nil if you did not replicate the continue logic |
| 17:01 | maacl | Help oh help, just upgraded my emacs packages and now I don't get a repl buffer when I do cider-jack-in or cider-connect |
| 17:02 | justin_smith | maacl: delete your elc files and restart emacs |
| 17:02 | justin_smith | cider tends to not upgrade cleanly |
| 17:03 | Graawr | justin_smith: yes, the continue will skip the current loop interation |
| 17:03 | maacl | justin_smith: Ok, will give that a try. |
| 17:04 | Graawr | justin_smith: but I get a nil from getInputStream whatever the input (file or directory jarEntry) |
| 17:08 | maacl | justin_smith: Thanks, that did the trick. |
| 17:08 | clojurebot | Huh? |
| 17:08 | justin_smith | maacl: np, it should really be in the cider docs |
| 17:11 | justin_smith | Graawr: so I made my own attempt to translate it, mostly just to help me read it |
| 17:12 | justin_smith | Graawr: the error I run into is that I get entries that belong in a specific directory, but not an entry representing the directory itself |
| 17:12 | justin_smith | (so of course it can't write to it, the directory has not been made yet) |
| 17:13 | justin_smith | Graawr: d'oh, and now I read the comments! |
| 17:14 | justin_smith | Graawr: would it be instructive to show you my translation that works, or would you rather sort it out yourself? |
| 17:14 | Graawr | justin_smith: I'd like to see your version if that's okay, I'm still a bit struggling halway through mine |
| 17:15 | justin_smith | of course I had to pick a jar that had more than 2800 files in it to test with... |
| 17:15 | justin_smith | OK |
| 17:16 | justin_smith | Graawr: this is a direct translation (taking into account the comments about .getParentFile) and it works https://www.refheap.com/97626 |
| 17:16 | Graawr | justin_smith: although I believe that fixing the fault reported in the comments shouln't be hard with raynes fs utilities, IIRC there's a function to make parents dirs if they don't exist |
| 17:16 | justin_smith | no need for any libs for this |
| 17:16 | justin_smith | getParentFiles is trivial |
| 17:16 | justin_smith | *getParentFile |
| 17:16 | Graawr | oh right |
| 17:17 | justin_smith | also, I changed the system separator to "/" because that works even on Windows |
| 17:17 | Graawr | ah, didn't know that |
| 17:17 | Graawr | hum |
| 17:17 | Graawr | I see you don't use enumeration-seq on the entries ? I thought it was needed |
| 17:17 | justin_smith | and I am quite reluctant to use system separator (perhaps superstitiously) after dealing with network code that tried to put system separator in a URL |
| 17:18 | justin_smith | Graawr: that's not a call, that's just a type |
| 17:18 | justin_smith | err... wait |
| 17:18 | justin_smith | why would I need enumeration-seq? |
| 17:19 | Graawr | nevermind, that was an artefact from a previous try where I got confused by the Enumeration class used |
| 17:19 | justin_smith | Graawr: oh, I get it. Since I am not using clojure sequence functions, I don't need to use enumeration-seq. |
| 17:20 | Graawr | yup |
| 17:20 | justin_smith | I directly converted the while into a while, instead of making it a doseq over a seq - which could have been cleaner, but I was in "zero brain translation" rote mode |
| 17:20 | Graawr | though if I wanted to use seq (like using a loop/recur with (first ...) and (rest ...)) it would be needed right ? |
| 17:20 | justin_smith | Graawr: I wouldn't use loop/recur - enumeration-seq + doseq |
| 17:21 | Graawr | oh |
| 17:21 | justin_smith | loop / recur with first / rest can almost always be translated into something higher level |
| 17:24 | Graawr | hum, yes, i've still not really wrapped my mind around higher-level looping structure besides map/reduce kinds |
| 17:24 | justin_smith | Graawr: you might find it a nice exercise to translate the while into a doseq / enumeration-seq combo, and/or replacing the innermost let with a with-open call on the two streams |
| 17:26 | Graawr | that's exactly what i'm experiencing ^^ |
| 17:28 | Graawr | by the way, as I'm extracting from a running jar, I use the (-> (class *ns*) .getProtectionDomain .getCodeSource .getLocation .getPath) to get the path of the running jar, but it, here again, seems a bit wobbly. Isn't there any standard tool to reference the running jar ? |
| 17:28 | justin_smith | also, I bet it could be at least 10x as fast if you added a byte-array for read, instead of reading one byte at a time |
| 17:28 | justin_smith | Graawr: what you have there is how I would do it |
| 17:28 | justin_smith | Graawr: what's the end goal? do you need the files to be on disk to use some OS utility on them? |
| 17:28 | Graawr | hum, I have to say my java IO was fairly brief, I might need to look further into bytes array for writing |
| 17:29 | justin_smith | Graawr: it's an alternate read method, takes a byte array for filling |
| 17:29 | justin_smith | and then of course instead of writing one byte, you would write as many bytes from the array as you read (the return value of read will tell you how many) |
| 17:30 | justin_smith | and write, of course, has an alternate method that takes a byte-array to write from |
| 17:31 | Graawr | justin_smith: no, i'm building a transcompiler for a school project; I convert a directory structure containing notes took in a given language into a local, static website, so I need to copy the default assets (in jar at runtime) to disk |
| 17:32 | Raynes | People keep telling me to rewrite refheap in node. |
| 17:32 | justin_smith | Graawr: OK, you may or may not be aware that we can use clojure.java.io/resource to get file contents from a jar without writing anything to disk |
| 17:32 | Raynes | I'm incredibly entertained by this, given that the one single lone reason for folks using refheap is because it's written in Clojure |
| 17:32 | Raynes | Because everyone has it in their head that "Hey, I can contribute to this in my favorite language!" |
| 17:33 | Raynes | But then they never contribute to it. |
| 17:33 | justin_smith | Raynes: nah, I use it because it isn't spammy and it syntax highlights with a dark background |
| 17:33 | Raynes | I need to rewrite it |
| 17:33 | rhg135 | nah, same here |
| 17:33 | Raynes | Not in node, but in Clojure. |
| 17:33 | Graawr | justin_smith: yes, but as I generate a static site, some assets (js, fonts, css) need to be on disk for the final "product" to be used by the end user |
| 17:33 | Raynes | justin_smith: So I should revert this commit to add interstitials? |
| 17:33 | justin_smith | Graawr: aha, so you aren't serving the stuff from a clojure web server |
| 17:33 | justin_smith | Raynes: DON"T YOU FUCKING JOKE |
| 17:33 | justin_smith | haha |
| 17:34 | Raynes | git branch -D piss-off-justin-smith |
| 17:34 | Raynes | gf3: We should rewrite refheap from the ground up with you designing. |
| 17:34 | Graawr | justin_smith: no, that's entirely local. The point is just to pass a lectures notes directory to the software, and it spits back a static site |
| 17:34 | Raynes | We'll use Graawr's static site generator. |
| 17:34 | Raynes | We'll just regenerate it every time someone makes a paste. |
| 17:35 | justin_smith | Graawr: ahh, got it |
| 17:35 | Raynes | In all seriousness, it might be fun to rewrite the whole thing with cljs |
| 17:35 | Raynes | Client side 4evr |
| 17:35 | justin_smith | http://i.imgur.com/aJwbNM4.jpg |
| 17:36 | Raynes | yes |
| 17:36 | rhg135 | Raynes, how would the api work if it ran client-side? |
| 17:36 | Raynes | How would anything work fi it ran client side? |
| 17:36 | Raynes | There'd still be a server component. |
| 17:37 | justin_smith | rhg135: the one benefit I can think of is it could push edits when people update the paste |
| 17:37 | Raynes | But the server would probably just become an API service. |
| 17:37 | justin_smith | perhaps even a UI to see revisions |
| 17:37 | rhg135 | ah ok, and that'd stay the same i assume? |
| 17:37 | Raynes | The API is shitty, so I'd probably change it too |
| 17:37 | rhg135 | i find it pretty simple |
| 17:37 | Raynes | I can add a compatibility layer :P |
| 17:38 | rhg135 | GET to /posts/:id get response |
| 17:38 | Raynes | Oh, that'd still work |
| 17:39 | rhg135 | i'm curious what you mean by a new api. |
| 17:40 | rhg135 | justin_smith, push is always win imo in web design |
| 17:40 | Raynes | rhg135: I mean, the problem with the current API is not really the API itself, but how it's written |
| 17:40 | Raynes | See, I didn't know a damn thing about web development. |
| 17:40 | Raynes | The API is entirely distinct from the interface. |
| 17:40 | Raynes | They hardly even use the same code |
| 17:40 | Raynes | It's so ridiculous. |
| 17:40 | Raynes | The web interface should be using the same API |
| 17:41 | rhg135 | oh I see, Raynes. So maybe some feature expressions if you go cljs? |
| 17:41 | Raynes | 'feature expressions'? |
| 17:41 | Raynes | Maybe I still don't know shit about web development. |
| 17:41 | Raynes | Damn it. |
| 17:41 | Raynes | I need to stop writing websites |
| 17:41 | Raynes | ;) |
| 17:42 | rhg135 | being able to share code server-side with client-side |
| 17:42 | Raynes | I see |
| 17:42 | Raynes | I don't know what state of the art in cljs client-server communication is |
| 17:42 | Raynes | But I hope there is some good shit available |
| 17:42 | Raynes | I'm spoiled by Meteor |
| 17:42 | Raynes | Where it's all so seamless |
| 17:42 | justin_smith | Raynes: feature-expressions are all liek "this code only works in js" "this other code only works in the jvm" but it's all still in one file |
| 17:42 | rhg135 | websockets i hear are useful for this |
| 17:43 | Raynes | Yeah, websockets are the answer but I don't want to deal with them directly. I imagine there's a bunch of cljs stuff that already deals with htis. |
| 17:43 | Raynes | If not, I'll come back in a few months when there is :P |
| 17:43 | rhg135 | or even ajax |
| 17:43 | Raynes | http://hoplon.io/ |
| 17:43 | Raynes | Like this |
| 17:44 | rhg135 | the goal is to minimize the synchronization from client to server |
| 17:45 | Raynes | It should all be pretty simple |
| 17:46 | rhg135 | i don't do any web dev either |
| 17:46 | Raynes | I do web dev, but usually single page apps. |
| 17:46 | rhg135 | but i've read being able to share code--like nodejs--is a boon |
| 17:47 | rhg135 | so clj(s) is nice |
| 17:47 | Raynes | I've been doing a lot of ES6/Coffee lately. |
| 17:47 | Raynes | I don't share the same furious hatred for JS that others do. |
| 17:48 | rhg135 | i've done Coffee just because i cba to remember js's quirks |
| 17:48 | justin_smith | shall we burn the heretic, stone him, or simply force him to live with his corrupted and sad decisions? |
| 17:48 | Raynes | I mean, coffee shares those quirks. |
| 17:48 | rhg135 | it's a fine language if you know it, and I don't |
| 17:49 | Raynes | Coffee is just a js preprocessor, really. |
| 17:49 | rhg135 | Raynes, but less |
| 17:49 | Raynes | ES6 is pretty cool too |
| 17:49 | rhg135 | it is which is great |
| 17:49 | justin_smith | rhg135: it was written in like a week, you can learn the important aspects in less time than that |
| 17:49 | Raynes | I'm furious they didn't add a thin arrow with their fat arrow. |
| 17:49 | rhg135 | ive been out of the js loop since before es6 existed |
| 17:49 | rhg135 | well it did just not practically |
| 17:50 | rhg135 | justin_smith, i could, it's the context switches in my mind I'd have to make going back to another lang |
| 17:51 | Raynes | ES6 offers: a module system, => fat arrow functions that pass the this context in, potentially comprehensions... |
| 17:52 | rhg135 | i didn't feel like running a development nodejs build |
| 17:52 | Raynes | babeljs.io |
| 17:52 | rhg135 | or using that lol |
| 17:52 | Raynes | https://github.com/Raynes/barrelroll |
| 17:53 | Raynes | Currently writing a thing with it |
| 17:55 | Raynes | oshi |
| 17:55 | Raynes | There's a new Clojure build tool? |
| 17:55 | rhg135 | why be afraid? |
| 17:56 | justin_smith | I don't know how mature boot is |
| 17:56 | rhg135 | not like they can't speak to each other |
| 17:56 | rhg135 | they both produce jars |
| 17:59 | Raynes | I wrote small very small portions of leiningen |
| 17:59 | Raynes | I'm emotionally attached |
| 17:59 | rhg135 | i like leiningen; it makes it easy for clj |
| 17:59 | rhg135 | cljs otoh is a pita |
| 18:12 | thsig | Hey guys, I'm about to start a web app project which I expect will eventually be quite full-featured - do people here prefer ring or pedestal or something else for such cases? Is ring mostly preferred for smaller apps, or is it most popular in general? |
| 18:13 | thsig | I.e. is ring preferred for smaller, simpler apps, and are other methods preferred for bigger/more complex apps? Or is it a good fit for the whole spectrum? |
| 18:16 | justin_smith | thsig: unless you have a really compelling reason, ring is the best bet |
| 18:16 | justin_smith | almost everything else (with rare exceptions like pedastal) uses ring |
| 18:18 | thsig | justin_smith: Thanks for the feedback! I'm coming at this from ruby land, having mostly used clojure for non-web stuff, so I was wondering how apt the analogy "ring ~ sinatra" was. |
| 18:18 | thsig | since in ruby, one ends up adding a lot of the stuff from rails back on to sinatra as the requirements get more complex |
| 18:19 | justin_smith | thsig: I wouldn't compare ring to sinatra, no |
| 18:19 | thsig | but of course clojure is different, just trying to get a feel for the web library landscape |
| 18:20 | justin_smith | thsig: if anything I would compare compojure to sinatra, but in the clojure world we tend to do things very modular |
| 18:20 | justin_smith | you can easily use ring without compojure |
| 18:21 | thsig | justin_smith: Ok, I see. I guess ring is more like rack. |
| 18:21 | thsig | there's a lot of stuff that comes along with rails that I've never used or liked |
| 18:21 | justin_smith | yeah, that's a much closer comparison |
| 18:21 | thsig | and now that I'm experienced I like the idea of picking just what I need |
| 18:21 | justin_smith | yeah, that's the norm in clojure |
| 18:23 | thsig | justin_smith: Thanks again for responding, I'll keep working through my checklist of libs that I need - looks like the ecosystem is pretty solid these days. |
| 18:24 | justin_smith | in my experience you can start with the libs you need for your basic functionality, and add more as you need them. We don't really do "frameworks" that demand you structure your entire app differently, for the most part |
| 18:25 | justin_smith | heh |
| 18:28 | gfredericks | ,(def a-million (bit-shift-left 20 1)) |
| 18:28 | clojurebot | #'sandbox/a-million |
| 18:29 | justin_smith | ,a-million |
| 18:29 | clojurebot | 40 |
| 18:34 | underplank | So im using stuart Sierras component for an compojure app. I dont need to store any state durably (ie db or anything). And I need to access the state in each ring handler. So I’ve manged to pass the component defrecord into the handler, but of course when I update it I get a new version and need to somehow return the new value of the component. |
| 18:34 | underplank | example code https://www.refheap.com/97630 |
| 18:37 | justin_smith | so every time a client hits create-proxy you would rebuild your whole system? that seems a bit silly |
| 18:38 | justin_smith | why not update an atom containing proxies? |
| 18:39 | justin_smith | otherwise you have a topological problem, as you've noticed, where the handler that creates your response also wants to return a new component |
| 18:39 | underplank | Sorry. the code is slightly wrong. So I have an Api component. And a Controller component. the controller component sits inside the Api component. When I add a proxy its supposed to add to the Controller component |
| 18:40 | underplank | I was thinking that the reference to the Controller component inside the Api Component needs to be an atom? and then I can update that at will. |
| 18:40 | underplank | and the Api component doesnt need to be replaced. |
| 18:40 | justin_smith | right. Or you can do something more convoluted where you both return the response and the potentially updated component, and then sort out that logic at a higher level of the code |
| 18:41 | justin_smith | but that seems like it would get a little messy without much benefit |
| 18:43 | underplank | yeah. I think I had already put in place the seperation of the API which is just poking the controller. So atom in the Api it is. thanks justin_smith ! |
| 20:11 | maacl` | Does anyone know if it is possible to get popup documentation with CIDER and company completion? |
| 20:13 | justin_smith | yes, it is possible |
| 20:14 | arrdem | supposedly it'll "just work" if you have company turned on |
| 20:14 | arrdem | hasn't worked for me in a while |
| 20:14 | justin_smith | that was my experience a while back, but I wouldn't be surprised if something broke at aome point |
| 20:14 | maacl` | justin_smith: Ok, is there anything I have to do to enable it? Completion works, but I am not getting any documentation |
| 20:15 | justin_smith | I'm not really sure, I just know I have had it working in the past |
| 20:15 | maacl` | justin_smith: ok |
| 20:16 | arrdem | #clojure-emacs may be able to help, but that's a pretty quiet channel. If it isn't in the CIDER docs, it's presumed to "just work" and you should probably open a ticket. |
| 20:22 | maacl` | arrdem: Thanks, I have raised a ticket. |
| 20:23 | arrdem | clojurebot: rejoice |
| 20:23 | arrdem | maacl`: thanks. |
| 20:23 | arrdem | maacl`: FWIW cider-grimoire still totally works, not that I'm biased or anything :P |
| 20:55 | doright | How do I convert, for example, "String" to a parameter of the (class ....) function? |
| 20:55 | arrdem | doright: what are you trying to achieve? resolve the class named by a string? |
| 20:56 | doright | arrdem: (defn get-meths [class-name] (for [meth (.getDeclaredMethods (class class-name))] (println (.getName meth)))) |
| 20:56 | doright | (defn get-meths [class-name] (for [meth (.getDeclaredMethods (class class-name))] (println (.getName meth)))) |
| 20:57 | doright | arrdem: With (import 'java.lang.reflect.Method) |
| 20:58 | Bronsa | doright: given that you call (class ) on class-name you're going to need an instance object of that class |
| 21:02 | doright | Bronsa: Not really. This works: (let [mths (.getDeclaredMethods (class String))] (for [m mths] (println (.getName m)))) |
| 21:02 | Bronsa | ,(class String) |
| 21:02 | clojurebot | java.lang.Class |
| 21:02 | Bronsa | doright: that's likely not what you want |
| 21:03 | doright | Bronsa: Why? It lists all the methods, like I asked. |
| 21:03 | Bronsa | doright: it lists all the methods of Class not String |
| 21:04 | doright | Bronsa: Ah, will have another look :) |
| 21:04 | arrdem | I'll leave you in Bronsa's more classloader-informed hands |
| 21:04 | Bronsa | doright: what is exactly that you want get-methods to do? |
| 21:05 | Bronsa | get-meths, rather |
| 21:06 | doright | Bronsa: Should have been: (let [mths (.getDeclaredMethods (class (String.)))] (for [m mths] (println (.getName m)))) |
| 21:07 | Bronsa | doright: what's the input to your function and what's the desired output? |
| 21:07 | doright | Bronsa: I don't think it's do-able but pass in a class name such as String or URL. |
| 21:08 | Bronsa | doright: as a string? |
| 21:08 | doright | Bronsa: (class (classname.)) [pseudo-code] looks even less likely |
| 21:08 | doright | Bronsa: Yes, as a string. |
| 21:09 | doright | Bronsa: S'pose core.typed might come in handy here. |
| 21:10 | doright | Bronsa: I'm just playing with Java interop, that's all, as a learning exercise. |
| 21:11 | Bronsa | doright: this is how it should be written then: http://sprunge.us/BUGY?clj |
| 21:11 | doright | Bronsa: There's a long-winded Java example of getting all the methods of a class so thought I'd have a go in Clojure. |
| 21:12 | doright | Bronsa: Marvelous. I'll go study that one. Thanks. |
| 21:12 | Bronsa | doright: that assumes your class-name string represents either an imported class or a fully qualified class name. Not sure why you can't just pass the class directly but that's how to do it with a string |
| 21:12 | Bronsa | doright: also note that for is lazy, when you need to perform side-effects you should use doseq |
| 21:15 | doright | Bronsa: (symbol "class-name-string") was what I needed. |
| 21:16 | Bronsa | doright: not really, (symbol "String") returns 'String as a symbol, not the String class |
| 21:16 | Bronsa | ,(class (symbol "String")) |
| 21:16 | clojurebot | clojure.lang.Symbol |
| 21:16 | doright | Bronsa: Maybe I could have passed it directly after all. |
| 21:16 | Bronsa | ,(class String) |
| 21:16 | clojurebot | java.lang.Class |
| 21:16 | Bronsa | doright: in that case http://sprunge.us/cOTN?clj and you can invoke it as (get-meths String) |
| 21:19 | doright | Bronsa: ^Class is a type hint? |
| 21:19 | Bronsa | yes |
| 21:19 | doright | Bronsa: Then that's what I was missing. Learned a lot, thanks. |
| 21:20 | Bronsa | doright: note that type hints only affect performances, they don't change the semantics of the code |
| 22:40 | justin_smith | ,(Class/forName "String") |
| 22:40 | clojurebot | #<ClassNotFoundException java.lang.ClassNotFoundException: String> |
| 22:40 | justin_smith | ,(Class/forName "java.lang.String") |
| 22:40 | clojurebot | java.lang.String |
| 23:31 | julianleviston | Which is more idiomatic, do you think? (into {} (filter pred m)) or (select-keys m (filter pred (keys m))) ? |
| 23:32 | justin_smith | julianleviston: the latter only gets to filter based on the keys... |
| 23:32 | justin_smith | but regardless, the former is nicer |
| 23:32 | julianleviston | yeah, that’s what I’m trying to do - filter on keys with a predicate... |
| 23:32 | julianleviston | justin_smith: why is it nicer, in your opinion? |
| 23:32 | justin_smith | less noise |
| 23:33 | julianleviston | justin_smith: this is the predicate: #(keyword? (key %)) |
| 23:33 | julianleviston | well, for the first one |
| 23:33 | justin_smith | (comp keyword? key) |
| 23:34 | julianleviston | justin_smith: actually I think you’ve answered my question… the second is better because it explains the intent more. |
| 23:34 | justin_smith | anyway, the into/filter is a more straightforward combo to my eyes, compared to select keys / filter / keys |
| 23:34 | justin_smith | OK |
| 23:34 | julianleviston | what I *really* want is a function select-keys-filter, so maybe I’ll go write that. |
| 23:34 | julianleviston | or something with a less ugly name |
| 23:35 | julianleviston | justin_smith: I guess that’s probably because the into/filter is more lazy. |
| 23:35 | julianleviston | justin_smith: damn clojure and its messy laziness :) |
| 23:35 | julianleviston | justin_smith: so, because it’s more lazy, it’s doing less work… :) right? that’s why you intuitively reach for it? |
| 23:36 | justin_smith | into isn't lazy |
| 23:36 | julianleviston | justin_smith: and I guess wrapping it in a function will name my intent, so convey the sematics better? |
| 23:36 | julianleviston | justin_smith: isn’t reduce lazy? |
| 23:36 | justin_smith | no |
| 23:37 | julianleviston | justin_smith: but it uses a transducer, doesn’t it? |
| 23:37 | julianleviston | justin_smith: I thought transducers were |
| 23:37 | julianleviston | justin_smith: I guess that’s only if you compose them, then their effect is lazy… hm... |
| 23:37 | justin_smith | nope, they are agnostic to laziness for the most part |
| 23:38 | justin_smith | the thing using them gets to decide if it is lazy or not |
| 23:38 | julianleviston | justin_smith: but if you compose two or more transducers, then the net effect is laziness of function application, isn’t it? |
| 23:38 | justin_smith | so a reduce on a transducer is eager, a sequence on a transducer can be lazy |
| 23:38 | justin_smith | absolutely not |
| 23:38 | julianleviston | guh. |
| 23:38 | julianleviston | that’s a bit sucky |
| 23:38 | justin_smith | what does lazy function composition even mean? |
| 23:39 | julianleviston | justin_smith: no, lazy application |
| 23:39 | justin_smith | OK, sequence on a transducer is lazy |
| 23:39 | julianleviston | justin_smith: if you compose two functions, and one of them is early aborting as in the case of filter, then its effect is laziness |
| 23:39 | justin_smith | reduce isn't |
| 23:39 | justin_smith | the transducer is neither lazy nor not lazy |
| 23:39 | julianleviston | justin_smith: yes, I know… but the fact that it can be means it is when it can be. |
| 23:39 | justin_smith | aborting early is not the same as laziness |
| 23:39 | justin_smith | reduce can return early, and is not lazy |
| 23:40 | julianleviston | justin_smith: hm… I guess we have different definitions then |
| 23:40 | justin_smith | reduce can have a filtered mapping as it's input, it's still going to be eager |
| 23:40 | julianleviston | justin_smith: eager meaning it gets all of its arguments before starting? |
| 23:40 | julianleviston | justin_smith: right? |
| 23:40 | justin_smith | early return isn't lazy, because it is never going to do the hypothetical work that followed - no work is delayed |
| 23:41 | julianleviston | justin_smith: yep. cool. |
| 23:41 | justin_smith | eager meaning it fully calculates its result when called |
| 23:41 | julianleviston | yep tho if the work IS INSIDE the transducer, not outside it, it *is* lazy… as in… lazy application… not lazy evaluation. |
| 23:41 | julianleviston | justin_smith: know what I mean? |
| 23:41 | justin_smith | absoluttely not, no, that makes no sense to me |
| 23:41 | justin_smith | the transducer is neither lazy nor not-lazy |
| 23:42 | justin_smith | it's a transduce |
| 23:42 | justin_smith | something lazy or eager can use it |
| 23:42 | julianleviston | justin_smith: lazy function application means you don’t apply the function across the whole thing if you don’t have to… |
| 23:42 | julianleviston | justin_smith: lazy usually just means the arguments are evaluated before the function begins. |
| 23:42 | justin_smith | are you talking about eagerly consuming args? that's not the same as laziness. |
| 23:42 | justin_smith | or lack thereof |
| 23:42 | julianleviston | justin_smith: hehe :) as I said, we have different definitions of lazy application here. |
| 23:42 | justin_smith | (first x) does not fully realize its input. It also is in no way lazy. |
| 23:43 | julianleviston | justin_smith: and yes… call it eager consumption, if you like. |
| 23:43 | justin_smith | julianleviston: your definition of lazy is not used anywhere in clojure |
| 23:43 | ddellacosta | julianleviston: it seems like you are redefining terms idiosyncratically, which does no one any good |
| 23:43 | julianleviston | no not at all. |
| 23:43 | julianleviston | I explicitly said lazy function application, not lazy evaluation. |
| 23:43 | justin_smith | there is no such thing as "lazy function application" in clojure |
| 23:43 | ddellacosta | what is the difference between application and evaluation? |
| 23:44 | julianleviston | ddellacosta: (apply + [1 2 3]) versus (+ 1 2 3) |
| 23:44 | justin_smith | what he means by his invented term "lazy function application" is that the args are not greedily consumed |
| 23:44 | ddellacosta | and, if it exists, please point us to some literature where this terminology is used |
| 23:45 | julianleviston | ddellacosta: it’s the difference between eval and apply, isn’t it? |
| 23:45 | ddellacosta | julianleviston: those two are exactly the same in terms of how they are evaluated. There's nothing "lazy" going on here |
| 23:45 | julianleviston | justin_smith: yeah! :) |
| 23:45 | ddellacosta | so we are talking about code vs. data? Okay, well, I've never heard lazy used in this context |
| 23:45 | julianleviston | ddellacosta: ddellacosta in that case, sure, but if you quote it, then they can be different… right? :) |
| 23:46 | julianleviston | ddellacosta: but of course you and justin are completely correct… |
| 23:47 | julianleviston | ddellacosta: in clojure, this makes no difference. |
| 23:47 | julianleviston | it’s important if you want to do less work, though... |
| 23:47 | justin_smith | I don't think the usage of the term lazy is helpful here really. "lazy evaluation" is a thing that exists - see Haskell. And Clojure uses lazy sequences. |
| 23:47 | julianleviston | justin_smith: ok I won’t use it then. |
| 23:47 | ddellacosta | julianleviston: the main thing is that using an idiosyncratic definition for something--even if you can make yourself understood, eventually--can make it hard to express your point to others |
| 23:47 | tomjack | isn't it "call by name"? |
| 23:47 | julianleviston | ddellacosta: sure. |
| 23:47 | ddellacosta | tomjack: is what "call by name?" |
| 23:48 | justin_smith | julianleviston: maybe what you are pointing out is just a side effect of the fact that the last arg to apply can be lazy. And sure, if the function isn't eager to consume that arg, it can be lazy on its args in that sense (though + is going to consume all its args when invoked) |
| 23:48 | julianleviston | So… back to the actual discussion, though… |
| 23:48 | tomjack | "call by name" is, I think, a maybe less idiosyncratic way to say "lazy function application" |
| 23:49 | tomjack | as opposed to clojure's "call by value" |
| 23:49 | justin_smith | tomjack: ahh, the way Haskell compiles / evaluates with term rewriting, I can see that |
| 23:49 | justin_smith | yeah, Clojure does not share those semantics at all |
| 23:50 | julianleviston | justin_smith: because it’s not super lazy… just sometimes lazy… when you specify. |
| 23:50 | justin_smith | I mean you can fake it by using delay all over the place but it will be ugly as fuck |
| 23:50 | justin_smith | julianleviston: it has lazy sequences |
| 23:50 | justin_smith | that's the beginning and end of laziness in clojure |
| 23:50 | julianleviston | what I was interested in, is minimizing the amount of work to do, generally… I mean, composing map with some, why map the entire list if we’re only going to use half of it later? |
| 23:50 | justin_smith | no lazy evaluation |
| 23:50 | julianleviston | justin_smith: yep. |
| 23:51 | tomjack | huh, some with map is just Clojure's normal laziness, no? |
| 23:51 | julianleviston | so if I make a transducer of partial map with partial some, it *should* be lazy… but is it? does it work like a unix pipe, or does it just work like ruby? (Doing the whole map first) |
| 23:52 | justin_smith | you could say that reduce / reduced meet some other goal of not fully evaluating inputs that laziness does, but then you should consider that every programming language has some version of this (usually the return or continue statements) |
| 23:52 | julianleviston | which brings me back to… which is better for early aborting / saving calculations on large lists… (into {} (filter pred m)) or (select-keys m (filter pred (keys m))) ? I guess it makes no difference because neither are |
| 23:52 | julianleviston | (sorry sub collections in there where I said lists…) |
| 23:53 | justin_smith | julianleviston: the transducer with composition ensures that all the steps are done together for one input. Whether this is eager or lazy is decided by the thing consuming the transducer in most cases. |
| 23:53 | julianleviston | justin_smith: that was exactly what I thought… brilliant :) |
| 23:53 | julianleviston | justin_smith: but of course, into and silect-keys … both of them aren’t composable in that way, so it makes no difference, correct? |
| 23:54 | julianleviston | select* |
| 23:54 | justin_smith | sure, and both are fully eager |
| 23:54 | julianleviston | I guess select-keys is probably more “efficient”, but it’s only a guess. |
| 23:55 | justin_smith | it's easy to test that with criterium |
| 23:55 | julianleviston | true! |
| 23:55 | julianleviston | will do |
| 23:55 | justin_smith | probably looks different with different input sizes |
| 23:55 | julianleviston | ,(time (let [m {:a "hey" :b "yes" "string-key" "should not include"} pred #(keyword? (key %))] (into {} (filter pred m)))) |
| 23:55 | julianleviston | , (time (let [m {:a "hey" :b "yes" "string-key" "should not include"} pred #(keyword? %)] (select-keys m (filter pred (keys m))))) |
| 23:55 | clojurebot | "Elapsed time: 7.390301 msecs"\n{:b "yes", :a "hey"} |
| 23:55 | clojurebot | "Elapsed time: 0.19605 msecs"\n{:a "hey", :b "yes"} |
| 23:55 | julianleviston | hm... |
| 23:55 | julianleviston | interesting |
| 23:56 | julianleviston | justin_smith: yeah, should test with a variety of coll sizes. |
| 23:57 | julianleviston | (inc justin_smith) |
| 23:57 | lazybot | ⇒ 191 |
| 23:57 | julianleviston | (inc ddellacosta) |
| 23:57 | lazybot | ⇒ 8 |
| 23:57 | julianleviston | (inc tomjack) |
| 23:57 | lazybot | ⇒ 5 |
| 23:58 | ddellacosta | hmm, I'm kind of surprised those two would differ at all efficiency-wise |
| 23:58 | ddellacosta | although actually the second one may have benefitted from caching from the first run |
| 23:58 | justin_smith | ddellacosta: the cost of consing up (count m)-n keys vs the cost of removing n keys |
| 23:59 | julianleviston | yep. |
| 23:59 | julianleviston | makes sense to me… |
| 23:59 | julianleviston | because of how into works |
| 23:59 | julianleviston | (source into) |
| 23:59 | julianleviston | ,(source into) |
| 23:59 | clojurebot | Source not found\n |
| 23:59 | ddellacosta | I guess into is O(n) |
| 23:59 | ddellacosta | yeah, into is a reduce |
| 23:59 | julianleviston | (reduce conj to from) |
| 23:59 | ddellacosta | select-keys is O(n log n)? |