2015-09-09
| 00:14 | ska-fan | Hmm, how do I produce either [a b c] or [a c] depending on some condition without mentioning a or c twice (i.e. not (if cond? ([a b c]) ([a c])))? Or is that an achievement that is worth nothing in functional world? |
| 00:15 | justin_smith | ska-fan: ##(if false [a b c] [a c]) -- your version has way too many parens |
| 00:15 | Bronsa | ska-fan: you'll have to go with the repetition |
| 00:16 | ska-fan | Bronsa: I thought so. Thanks :) |
| 00:18 | ska-fan | justin_smith: Thanks! Learning two languages at a time (the other is racket) I get confused :) |
| 00:25 | ska-fan | Any other way to make this more concise? https://gist.github.com/mbertheau/6f7debba1a8943930c13 |
| 00:26 | justin_smith | ska-fan: you can define final-style within the outer let block |
| 00:26 | justin_smith | you don't need nested let for that |
| 00:26 | bufh | hi |
| 00:27 | justin_smith | (that is, base style and final style can be moved into the top level let, before normal-bar) |
| 00:28 | bufh | I am just beginning to learn clojure - can someone point out ways of making this code more efficient? (https://gist.github.com/singhgurjeet243/208a345c17e8a35fc0be) |
| 00:30 | justin_smith | bufh: you could use a transducer instead of reduce and map - unlike a transducer, the map produces a lazy seq you don't need |
| 00:31 | bufh | justin_smith: are there examples of transducers you can point me to? |
| 00:32 | justin_smith | bufh: this is the best intro http://clojure.org/transducers |
| 00:32 | bufh | justin_smith: thanks! |
| 00:32 | TEttinger | it's not quite a beginner thing, transducers, but I think that's mostly because they are, relatively speaking, new. there's nothing terribly complex about them as a concept I think |
| 00:32 | justin_smith | so instead of reduce + (map f c) you can use transduce + (map f) + c |
| 00:33 | justin_smith | TEttinger: well, he was asking about performance, which isn't a beginner thing either is it? |
| 00:33 | TEttinger | I suppose. transducers do seem like a good route here |
| 00:33 | bufh | I am not a CS beginner, just a clojure beginner :) |
| 00:34 | justin_smith | bufh: transducers are pretty unique, and are lower level and more specific than people seem to expect |
| 00:34 | bufh | justin_smith: seems like it, I haven't seen them in scala or haskell |
| 00:34 | justin_smith | they replace using a sequence as the universal abstraction with a more general "source of data" and "consumer of data" abstraction |
| 00:35 | justin_smith | for things like channels, or multi step transforms, where fully implementing sequences is just overhead |
| 00:35 | justin_smith | bufh: haskell doesn't need it because they have a clever compiler, clojure is intentionally unclever |
| 00:35 | bufh | is there a 'zen of clojure' a-la python? |
| 00:35 | justin_smith | there's definitely a unique clojure approach |
| 00:36 | justin_smith | stuff on clojure.org, plus conj.io, plus examples and suggestions here on IRC will help you figure it out pretty quick I think |
| 00:36 | justin_smith | http://conj.io/ |
| 00:37 | bufh | sweet |
| 00:38 | TEttinger | justin_smith: interestingly, when I looked at bufh's the code the first thing I thought was "this could be faster if it used mutable state in sample". I don't know if that's accurate though |
| 00:38 | justin_smith | I don't think mutation would give any advantage in there that a transducer wouldn't |
| 00:39 | justin_smith | bufh: also, just for a lark you could try explicitly using first and second in the definition of test-point - destructuring has a bit of overhead you might want to avoid in a bottleneck |
| 00:40 | TEttinger | well there's some obvious speedup in using unchecked math and making sure the numbers are type-hinted |
| 00:40 | TEttinger | or merging test-point and gen-point into one fn since they're always together |
| 00:40 | justin_smith | oh yeah, some type hints in a few places would likely help, yeah |
| 00:41 | bufh | hm, I don't know how to use type hints yet |
| 00:41 | TEttinger | you'll want that reflection warning for math thing that got added in 1.7 |
| 00:41 | justin_smith | bufh: http://clojure.org/java_interop#Java Interop-Type Hints |
| 00:41 | justin_smith | oh man, that's not a real link |
| 00:41 | TEttinger | it would show you where numbers are being boxed (objects, reference types, heavier weight) or are being calculated with primitives |
| 00:42 | justin_smith | http://clojure.org/java_interop#Java%20Interop-Type%20Hints |
| 00:42 | TEttinger | this one http://clojuredocs.org/clojure.core/*unchecked-math* |
| 00:43 | TEttinger | math-heavy code can be unusually slow in clojure if you aren't aware of how boxing works and when you want to avoid it |
| 00:44 | TEttinger | boxing is needed to put a primitive int into a collection of any kind, where it becomes an Integer |
| 00:44 | puredanger | A general rule of thumb is that boxed math is about 100x slower than primitive math |
| 00:44 | bufh | got it, I understand boxing/unboxing on the JVM, but didn't know how to use it in clojure |
| 00:44 | justin_smith | 100 is a big number |
| 00:45 | TEttinger | ,36r100 ;; is bigger |
| 00:45 | clojurebot | 1296 |
| 00:45 | bufh | so, how can I ensure, that e.g. gen-point generates an array of double in the JVM as opposed to an array of Double |
| 00:45 | bufh | ? |
| 00:45 | TEttinger | that would be two parts. |
| 00:45 | justin_smith | bufh: well, you would need to use an array first |
| 00:45 | justin_smith | you can't put primitives in vectors |
| 00:46 | justin_smith | (well, not in normal ones) |
| 00:46 | TEttinger | they can be put in, they will just change to boxed versions, like int to Integer |
| 00:46 | justin_smith | TEttinger: potato, potato |
| 00:47 | TEttinger | you're right about the array thing, and arrays are I think about as fast in clojure as they are in java :) |
| 00:49 | justin_smith | bufh: or, instead of an array or vector, you can use vector-of http://conj.io/store/v1/org.clojure/clojure/1.7.0/clj/clojure.core/vector-of/ |
| 00:49 | justin_smith | in fact I think vector-of is probably the nicest option here |
| 00:49 | justin_smith | (other than unrolling the code so you don't need a collection at all) |
| 00:50 | puredanger | It only helps on memory |
| 00:50 | puredanger | Primitive vecs still box in and out |
| 00:50 | justin_smith | puredanger: vector-of doesn't help with the boxing issue? |
| 00:50 | TEttinger | http://clojuredocs.org/quickref under java interop |
| 00:50 | justin_smith | oh |
| 00:50 | justin_smith | never mind then! |
| 00:50 | amalloy | ska-fan: another option is `[~a ~@(when cond? [b]) ~c] |
| 00:50 | TEttinger | ah! |
| 00:51 | ska-fan | amalloy: Hmm! Is that considered hacky? |
| 00:51 | amalloy | but that is some fancypants unquoting stuff that may just be distracting |
| 00:51 | amalloy | *shrug* some people don't seem to like it. i am perfectly happy with it myself, and use it when it's convenient |
| 00:51 | justin_smith | ska-fan: it will make people think "hey, this guy is clever, like amalloy" |
| 00:51 | TEttinger | amalloy has some rather fancy pants |
| 00:52 | justin_smith | oh, not much, just on IRC, discussing some guy's pants |
| 00:53 | TEttinger | it's interesting; ~@ is rather often a potential part of a solution but not often used outside of things that are already loaded with macro goodness |
| 00:53 | ska-fan | I have this now: https://gist.github.com/mbertheau/6f7debba1a8943930c13 I guess I could go (let [be (if cond [b] [])] (concat [a] be [c])) but that doesn't strike me as particularly beautiful either. |
| 00:54 | TEttinger | ,(concat [:a] (if true :b) [:c]) |
| 00:54 | clojurebot | #<IllegalArgumentException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Keyword> |
| 00:54 | TEttinger | ,(concat [:a] (if true [:b]) [:c]) |
| 00:54 | clojurebot | (:a :b :c) |
| 00:54 | TEttinger | ,(concat [:a] (if false [:b]) [:c]) |
| 00:54 | clojurebot | (:a :c) |
| 00:54 | justin_smith | TEttinger: yeah, that's what the ~@ trick expands to, right? |
| 00:54 | TEttinger | not sure |
| 00:54 | ska-fan | ,[:a (if false :b) :c] |
| 00:54 | clojurebot | [:a nil :c] |
| 00:55 | TEttinger | the trick I was pointing out was that if returns nil if there's no else |
| 00:55 | amalloy | justin_smith: more or less |
| 00:55 | ska-fan | Ah, concat skips nil. |
| 00:55 | amalloy | ska-fan: not exactly |
| 00:55 | justin_smith | ,`(:a ~(if false [:b]) :c) |
| 00:55 | clojurebot | (:a nil :c) |
| 00:55 | amalloy | concat treats things as sequences, and nil is an empty sequences |
| 00:55 | justin_smith | err |
| 00:55 | justin_smith | ,`(:a ~@(if false [:b]) :c) |
| 00:55 | clojurebot | (:a :c) |
| 00:55 | justin_smith | ,'`(:a ~@(if false [:b]) :c) |
| 00:55 | clojurebot | (clojure.core/seq (clojure.core/concat (clojure.core/list :a) (if false [:b]) (clojure.core/list :c))) |
| 00:55 | justin_smith | oh yeah, that's about the same :) |
| 00:56 | TEttinger | interesting |
| 01:00 | TEttinger | bufh, heh, we got side-tracked for a moment with another good yak to shave. but I noticed that you might not actually need the array |
| 01:00 | TEttinger | since all your code is doing is conditionally incrementing a value |
| 01:00 | bufh | ok, how? |
| 01:00 | justin_smith | TEttinger: yeah, that's what I was getting at with unrolling |
| 01:00 | justin_smith | bufh: by combining those two separate functions (or splitting more finely) |
| 01:00 | justin_smith | such that you don't need the tuple |
| 01:01 | justin_smith | because your boxing will be caused by the part where you put the x and y in a collection |
| 01:01 | ska-fan | Hmm, filterv but no removev |
| 01:02 | bufh | ok, let me try it |
| 01:12 | bufh | hmm, so no appreciable speedup |
| 01:12 | bufh | https://gist.github.com/singhgurjeet243/208a345c17e8a35fc0be |
| 01:13 | justin_smith | bufh: oh wait I just realized why you call double on line 25 |
| 01:14 | justin_smith | bufh: if you are doing rational math, the unboxing isn't possible - rational math is always slow, and there is no such thing as an unboxed rational |
| 01:14 | bufh | ok, but the rational math only enters in that line - after the heavy lifting is already done |
| 01:14 | justin_smith | oh... maybe the rational is only being produced in that one place though |
| 01:14 | justin_smith | haha, yeah |
| 01:15 | justin_smith | anyway, style wise I prefer (* 4.0 e) to (double (* 4 e)) |
| 01:15 | bufh | infact the new sampler is slower |
| 01:15 | bufh | fair point |
| 01:15 | bufh | on style |
| 01:15 | justin_smith | oh, interesting... |
| 01:16 | bufh | but not by much |
| 01:16 | bufh | my suspicion is on the range there |
| 01:16 | justin_smith | I wonder if there's a place where hinting would help there... |
| 01:17 | amalloy | bufh: if you use (time) for profiling you will get very unreliable results |
| 01:17 | amalloy | it's just for getting a rough idea of how long stuff is taking |
| 01:17 | amalloy | you can get reasonable results from criterium instead, if you need to time things |
| 01:17 | bufh | ok, but if one approach was 10x faster, that'd probably show up, right? |
| 01:18 | amalloy | wellll, maybe. the jvm can do some strange things. often the second one you run is faster, because of JITing, or maybe the first one is faster because of GC... |
| 01:18 | amalloy | 10x is a lot, and i would expect that to usually correlate with actually being faster |
| 01:18 | amalloy | but no guarantees, especially if it's like 5us vs 50us |
| 01:18 | ska-fan | Ha! (-> [:a] (into (if cond? [:b])) (into [:c])) What do you think? |
| 01:18 | justin_smith | ,(transduce + (map inc) [1 2 3 4]) |
| 01:18 | bufh | ➜ pi lein run |
| 01:18 | bufh | "Elapsed time: 1510.67 msecs" |
| 01:18 | bufh | "Elapsed time: 1309.328 msecs" |
| 01:19 | clojurebot | #error {\n :cause "Wrong number of args (0) passed to: core/map/fn--4537"\n :via\n [{:type clojure.lang.ArityException\n :message "Wrong number of args (0) passed to: core/map/fn--4537"\n :at [clojure.lang.AFn throwArity "AFn.java" 429]}]\n :trace\n [[clojure.lang.AFn throwArity "AFn.java" 429]\n [clojure.lang.AFn invoke "AFn.java" 28]\n [clojure.core$transduce invokeStatic "core.clj" 6573]\... |
| 01:19 | amalloy | bufh: right, so that's easily within experimental error |
| 01:19 | justin_smith | ,(transduce + (map inc) 0 [1 2 3 4]) |
| 01:19 | clojurebot | #error {\n :cause "Cannot cast clojure.core$map$fn__4537 to java.lang.Number"\n :via\n [{:type java.lang.ClassCastException\n :message "Cannot cast clojure.core$map$fn__4537 to java.lang.Number"\n :at [java.lang.Class cast "Class.java" 3176]}]\n :trace\n [[java.lang.Class cast "Class.java" 3176]\n [clojure.core$cast invokeStatic "core.clj" 338]\n [clojure.core$_PLUS_ invokeStatic "core.clj" ... |
| 01:19 | amalloy | criterium exists to give you a little more confidence in these estimates |
| 01:20 | amalloy | ,(doc transduce) |
| 01:20 | clojurebot | "([xform f coll] [xform f init coll]); reduce with a transformation of f (xf). If init is not supplied, (f) will be called to produce it. f should be a reducing step function that accepts both 1 and 2 arguments, if it accepts only 2 you can add the arity-1 with 'completing'. Returns the result of applying (the transformed) xf to init and the first item in coll, then applying xf to that result and ... |
| 01:20 | amalloy | ,(transduce (map inc) + 0 [1 2 3 4]) ; ?? |
| 01:20 | clojurebot | 14 |
| 01:20 | justin_smith | ahh, I had the first two flipped! |
| 01:21 | justin_smith | bufh: that's for you ^ amalloy's example shows how to adapt what you have to a transducer (won't be a huge speedup, but should be a speedup) |
| 01:22 | bufh | sorry, I need a bit more help : how do I convert sample2 to use transducers? |
| 01:22 | justin_smith | bufh: where the above has inc, put your own fn |
| 01:23 | justin_smith | and where it has a vector, replace your range call |
| 01:23 | justin_smith | otherwise it is exactly like amalloy 's example |
| 01:23 | justin_smith | bufh: transduce replaces reduce |
| 01:25 | bufh | i am getting a compilation error: Unable to resolve symbol: transduce in this context |
| 01:25 | justin_smith | bufh: clojure version? |
| 01:25 | TEttinger | are you using clojure 1.7 or newer? |
| 01:25 | TEttinger | ,*clojure-version* |
| 01:25 | clojurebot | {:major 1, :minor 8, :incremental 0, :qualifier "alpha3"} |
| 01:26 | TEttinger | you can check with that in the REPL |
| 01:26 | bufh | its 1.6 |
| 01:26 | bufh | ok, have to figure out how to upgrade |
| 01:26 | justin_smith | bufh: are you using leiningen? |
| 01:26 | bufh | yea |
| 01:26 | justin_smith | just change the version number in project.clj, clojure is just a library |
| 01:27 | justin_smith | bufh: also, with clojure 1.7 you can use *warn-on-boxed* for numeric ops |
| 01:29 | puredanger | (set! *unchecked-math* :warn-on-boxed) |
| 01:29 | justin_smith | :global-vars {*warn-on-reflection* true *unchecked-math* :warn-on-boxed} |
| 01:30 | justin_smith | ^ that for project.clj |
| 01:31 | bufh | wow boxing warnings across the board |
| 01:31 | luxbock | when you use (set! *unchecked-math* :warn-on-boxed) in a namsepace, does it apply only to that ns? |
| 01:31 | justin_smith | it is not namespace specific |
| 01:32 | justin_smith | it changes a var in clojure.core |
| 01:32 | luxbock | I would imagine having *unchecked-math* :warn-on-boxed turned globally on would be pretty annoying |
| 01:32 | luxbock | guess you'd only use it when tweaking some specific thing |
| 01:32 | justin_smith | luxbock: it's the only option - though you can turn it off while compiling libs and then turn it on while loading your own code I guess |
| 01:33 | justin_smith | luxbock: the true root of the annoyingness is the libs that force numeric boxing, if they fix their shit you won't get annoying messages :) |
| 01:33 | luxbock | hmm I see |
| 01:33 | justin_smith | luxbock: I'm being glib about that |
| 01:34 | luxbock | I wouldn't actually know how to fix the boxed warnings in bufh's code, for example in `gen-point` |
| 01:34 | luxbock | you'd just give type-hints for the return value? |
| 01:34 | justin_smith | luxbock: by using hints in the specific places where the compiler doesn't know the value can remain unboxed |
| 01:34 | bufh | ok I changed both samplers to xducers, both are 50% faster |
| 01:34 | justin_smith | nice! |
| 01:35 | ska-fan | What's short for (if (nil? a) 300 a) ? |
| 01:35 | justin_smith | the boxing warnings should help too, once you sort them out |
| 01:35 | justin_smith | (if-not a 300 a) |
| 01:35 | justin_smith | (or a 300) |
| 01:35 | justin_smith | that last one is my final answer |
| 01:36 | bufh | ok lots of boxing and unboxing going on - how do I eliminate it say in test-point ? |
| 01:36 | ska-fan | justin_smith: Thanks! |
| 01:38 | justin_smith | bufh: [[^double x ^double y]] |
| 01:38 | justin_smith | (in the args vector) |
| 01:38 | justin_smith | though the vector always boxes x and y, those hints can force unboxing in the expression |
| 01:39 | justin_smith | s/force/facilitate |
| 01:39 | TEttinger | I'm curious how this performs |
| 01:39 | TEttinger | ,(let [sqr (fn [^double d] (* d d)) check-point (fn [] (< (+ (sqr (Math/random)) (sqr (Math/random))) 1.0)) n 10000 total (int-array 1 0)] (dotimes [_ n] (if (check-point)(aset ^ints total 0 (unchecked-inc-int (aget ^ints total 0))))) (* 4.0 (/ (aget ^ints total 0) n))) |
| 01:39 | clojurebot | 3.15 |
| 01:40 | luxbock | justin_smith: what about `gen-point` in bufh's code? |
| 01:40 | luxbock | I get four boxed math warnings which I have no idea how I would eliminate |
| 01:40 | TEttinger | also I don't know how I'd type-hint sqr |
| 01:40 | justin_smith | luxbock: that shouldn't have any boxed math happening, but it is of course boxing the values |
| 01:41 | luxbock | Boxed math warning, blah.clj:3:4 - call: public static java.lang.Number clojure.lang.Numbers.unchecked_minus(java.lang.Object,long). |
| 01:41 | TEttinger | so there, the first arg in a subtraction is boxed |
| 01:42 | TEttinger | the second is a long already, so no worries about that |
| 01:42 | luxbock | ah I see, think I got it now |
| 01:43 | luxbock | yep |
| 01:43 | justin_smith | I wouldn't think that (rand) needs a type hint to avoid boxing |
| 01:44 | luxbock | it apparently does |
| 01:44 | justin_smith | weird, this must be set up wrong... |
| 01:45 | justin_smith | oops, wrong clj version in this local repl, don't mind me |
| 01:45 | bufh | ok this is another 50% faster https://gist.github.com/singhgurjeet243/208a345c17e8a35fc0be |
| 01:46 | justin_smith | bufh: is that just from adding the ^double hints? |
| 01:46 | bufh | 2 things: 1. replacing 2 and 1 with 2.0 and 1.0 in gen-point/gen-and-test-point and 2. by adding the two ^double hints |
| 01:47 | justin_smith | bufh: (defn gen-point [] [(- (* 2.0 ^double (rand)) 1.0) (- (* 2.0 ^double (rand)) 1.0)]) -- this should help too |
| 01:47 | justin_smith | (finally got my own reflection and boxing warning repl opened up) |
| 01:47 | justin_smith | funny that rand is not hinted / known by the compiler already |
| 01:48 | bufh | ok, only 2 box/un warnings and about the same speed |
| 01:48 | justin_smith | luxbock: you were right, I really didn't think rand would be unhinted |
| 01:49 | luxbock | is it faster to call (.nth this-vec 0) than `first`? |
| 01:49 | justin_smith | luxbock: that would be a good one for a criterium test |
| 01:50 | bufh | how do I fix these? |
| 01:50 | bufh | Boxed math warning, pi/core.clj:24:27 - call: public static java.lang.Number clojure.lang.Numbers.divide(java.lang.Object,long). |
| 01:50 | bufh | Boxed math warning, pi/core.clj:24:20 - call: public static double clojure.lang.Numbers.unchecked_multiply(double,java.lang.Object). |
| 01:50 | justin_smith | bufh: oh, and you can hint gen-and-test-point in the same way - I bet it would speed up even more |
| 01:51 | justin_smith | bufh: those two warnings are just the two one-time ops on line 24 |
| 01:51 | justin_smith | definitely not a bottleneck |
| 01:52 | bufh | hmm, so no way of making this code faster? |
| 01:52 | justin_smith | (though also very easy to fix with the same kind of type hints we've been doing so far) |
| 01:52 | justin_smith | faster rng |
| 01:52 | justin_smith | using java data structures instead of clojure ones |
| 01:53 | justin_smith | bufh: I've had a lot of vodka and whiskey tonight, there might be other tricks that I would think of in the morning |
| 01:54 | bufh | lol, let me not kill the buzz :) |
| 01:54 | justin_smith | oh no, it's all good |
| 01:56 | Bronsa | cool kids like to get high on clojure |
| 01:59 | justin_smith | freenode is where all the cool kids hang out https://twitter.com/stefhatcher/status/641488692809338880 |
| 01:59 | bufh | thanks for your help - this is the most useful discourse in any #lang group that I have ever been in |
| 02:02 | Bronsa | (inc justin_smith) |
| 02:02 | Bronsa | gah |
| 02:02 | justin_smith | that bot makes clojure look bad sometimes :P |
| 02:04 | amalloy | (dec lazybot) |
| 02:04 | lazybot | ⇒ 36 |
| 02:05 | Bronsa | (inc justin_smith) |
| 02:05 | lazybot | ⇒ 295 |
| 02:23 | bufh | ok, so the clojure implementation is only about 50% slower than the java implementation |
| 03:18 | luxbock | here is the benchmark game implementation of pi-digits in Clojure: http://benchmarksgame.alioth.debian.org/u32/program.php?test=pidigits&lang=clojure&id=5 |
| 03:19 | luxbock | slighly more verbose |
| 03:23 | luxbock | whoa, the Java version of the binary-tree benchmark is actually faster than C or C++ |
| 03:35 | chomwitt | goodmorning from greece. refer vs require. we can see in repl with ns-map changes we make to the current ns with refer. is there a command to do the same for require ? |
| 04:29 | TEttinger | luxbock, that's hilarious. the benchmarks game is really now "who can call an external library written in assembly the fastest" |
| 04:37 | tdammers | can I ask questions about Selmer here? |
| 04:46 | ddellacosta_ | tdammers: I don't see why not...question is more if anyone around now has any familiarity with it |
| 04:46 | Bronsa | luxbock: wow that is ridiculous |
| 04:50 | quii | Hello, I am a bit of an emacs noob and need some help. I've got quite comfortable using cider and have a *reasonable* workflow for doing TDD. However i really need some refactoring support. The consensus as far as I am aware is to use https://github.com/clojure-emacs/clj-refactor.el . However when I follow the instructions to install it, it seems to just break cider. I get “C-c C-k is undefined” when i try and run my tests. When I |
| 04:50 | quii | .. |
| 04:50 | quii | So obviously some kind of conflict but I’m not entirely sure where to start |
| 04:50 | quii | appreciate any kind of help |
| 04:51 | tdammers | ddellacosta_: right... |
| 04:51 | quii | yup :p |
| 04:51 | TEttinger | quii, it's a bit sleepy right now, stick around and maybe ask again in... 4 hours? that's when east coast US might start getting up |
| 04:51 | quii | ok :) |
| 04:51 | tdammers | so I'm trying to do {% if somevector|count <= 1 %} |
| 04:52 | tdammers | where somevector is a context variable that I know is a vector |
| 04:52 | tdammers | gives me a NullPointerException |
| 04:52 | TEttinger | tdammers: woah what syntax is hat... |
| 04:52 | tdammers | TEttinger: selmer |
| 04:53 | ddellacosta_ | tdammers: no idea how selmer works...but I'll ask some dumb questions to help debug: I'm assuming that that sums the values in the vector? Does it require more than 1 or for no nils to exist in the vector? |
| 04:54 | tdammers | the goal is to check whether the vector has 0 or 1 elements, or more |
| 04:54 | ddellacosta_ | oh, so count is counting the number of items? I see |
| 04:54 | tdammers | if it has 0 or 1, I want to generate a hidden field with the only valid value, otherwise I want to render a dropdown |
| 04:54 | tdammers | I tried {% if (somevector|count) <= 1 %}, but that gives me a parser error |
| 04:55 | tdammers | saying that (somevector|count) is not a valid filter |
| 04:55 | TEttinger | if it has 0 items, calling ##(seq []) may be trouble |
| 04:55 | lazybot | ⇒ nil |
| 04:55 | TEttinger | ,(count (seq [])) |
| 04:55 | clojurebot | 0 |
| 04:55 | TEttinger | hm |
| 04:55 | tdammers | ,(count nil) ; more interesting edge case |
| 04:55 | clojurebot | 0 |
| 04:55 | tdammers | but stil |
| 04:55 | tdammers | l* |
| 04:55 | chomwitt | in lein repl (clorure.s[tab] completes with clojure.set clojure.string etc. But i've not loaded them with 'require in the current user namespace |
| 04:56 | TEttinger | hm, nope https://github.com/yogthos/Selmer/blob/master/src/selmer/filters.clj#L226 |
| 04:56 | tdammers | wait, turns out {% if somevector|count <= 1 %} does in fact work |
| 04:57 | tdammers | the exception is caused by something else |
| 04:57 | tdammers | (trial-and-error debugging ftw) |
| 04:57 | TEttinger | is the paren thing maybe calling it somehow? |
| 04:57 | tdammers | no, if I add the parens, the parser gives a nonsensical error, it doesn't even get to run the template at all |
| 04:57 | tdammers | my guess is that it's a weird edge case in the parser |
| 05:00 | TEttinger | hm |
| 05:00 | TEttinger | scanning through this... https://github.com/yogthos/Selmer/blob/master/src/selmer/filter_parser.clj |
| 05:00 | TEttinger | I can't find any indication that it handles parens sensically |
| 05:00 | TEttinger | there's nothing like a grouping indicator |
| 05:01 | tdammers | I'd expect it outside of the filter parser |
| 05:01 | tdammers | but haven't found the expression parser yet |
| 05:05 | TEttinger | well whatever calls parse is doing something |
| 05:05 | TEttinger | eyyyy yogthos |
| 05:05 | TEttinger | tdammers has some selmer questions |
| 05:06 | TEttinger | https://github.com/yogthos/Selmer/blob/master/src/selmer/parser.clj#L238 tdammers, this looks like the only place it might start to handle parens, and even then it would be something that calls this... |
| 05:06 | tdammers | hmm, so far I've been figuring it'd be somewhere in the regexes that deconstruct tags |
| 05:06 | tdammers | util.clj, somewhere |
| 05:07 | tdammers | https://github.com/yogthos/Selmer/blob/master/src/selmer/util.clj#L46 |
| 05:07 | tdammers | somewhere around here |
| 05:09 | tdammers | https://github.com/yogthos/Selmer/blob/master/src/selmer/tags.clj#L141 |
| 05:09 | tdammers | looks like "if" is special |
| 05:10 | tdammers | so there's not really a notion of "expressions" here, is there? |
| 05:10 | tdammers | that would also explain why binding a seq to a local variable with {% with %} and then trying to iterate over it doesn't work |
| 05:11 | TEttinger | I guess? I don't do any web dev that isn't static webpages on github :) |
| 05:12 | TEttinger | like the show-off page for the large amounts of art I keep making. http://tommyettinger.github.io/home/PixVoxel/cu3/Firing.html |
| 05:15 | tdammers | looks nice |
| 05:15 | tdammers | page locks up firefox somehow though |
| 05:18 | TEttinger | hehe |
| 05:18 | TEttinger | it's a ton of gifs, it does that |
| 05:18 | tdammers | still locked |
| 05:18 | tdammers | and this isn't shitty hardware, mind you - fairly new i7 thinkpad |
| 05:19 | TEttinger | it's I believe a firefox bug |
| 05:19 | wasamasa | orly |
| 05:19 | wasamasa | loads slowly, but surely here in firefox |
| 05:19 | TEttinger | there's a page that has even more gifs that you can try wasamasa :) |
| 05:19 | TEttinger | http://tommyettinger.github.io/home/PixVoxel/cu3/index.html |
| 05:20 | tdammers | uhm, I think I'd rather not :D |
| 05:20 | tdammers | xkill ftw |
| 05:21 | TEttinger | I have no trouble with chrome |
| 05:21 | wasamasa | it always surprised me when a classmate can't even start chromium without it eating up all the memory and hanging on their L512 while I can use it on my X200s just fine |
| 05:21 | TEttinger | L512? |
| 05:21 | wasamasa | yeah, at least one generation newer CPU |
| 05:22 | wasamasa | mine has a C2D, his an i3 |
| 05:22 | TEttinger | huh... |
| 05:22 | wasamasa | the difference is that I'm using linux and he's got a windows install from work on it :D |
| 05:23 | TEttinger | it's weird why some computers can't handle these poorly-made gif spewing pages |
| 05:23 | TEttinger | I know someone with a 6-year-old linux netbook who can't load any pages I make with even a quarter that many gifs |
| 05:23 | TEttinger | he uses firefox I think for the reason you gave, chromium uses too much mem |
| 05:24 | wasamasa | I've got to admit, the second site got quite a bit more for downloading |
| 05:24 | wasamasa | both peter out on me once in a while |
| 05:24 | wasamasa | if it's firefox, it's high CPU usage, for chromium it's a memory leak |
| 05:24 | TEttinger | for some reason github hasn't warned me about using more than 1GB of repo space for the github pages repo that hosts all that |
| 05:24 | TEttinger | I am using about 4.5 GB now |
| 05:25 | tdammers | does git have a space limit? |
| 05:25 | TEttinger | github says it recommends staying under 1 GB but does not have a hard limit, and will send a polite email if you use too much |
| 05:26 | tdammers | right |
| 05:26 | TEttinger | gitlab has a hard 10GB limit for free accounts on their server |
| 05:26 | tdammers | which they will probably only start doing once you use an order of magnitude more, or when too many users start doing it |
| 05:26 | TEttinger | bitbucket no idea |
| 05:26 | tdammers | IIRC bitbucket has no limit at all |
| 05:26 | TEttinger | 10 TB, sure |
| 05:26 | tdammers | well, no explicit limit |
| 05:27 | tdammers | I bet the fine print has some sort of FUP |
| 05:27 | tdammers | bitbucket price-shapes on team size instead |
| 05:50 | Olajyd | Hi TEtttinger :D |
| 05:50 | TEttinger | hey Olajyd! |
| 05:50 | TEttinger | how's it going? |
| 05:51 | Olajyd | TEttinger, going well…had a week off for some job weldone the past few weeks |
| 05:51 | TEttinger | good good! |
| 05:51 | Olajyd | Yea :) |
| 05:52 | TEttinger | back into coding now though? |
| 05:52 | Olajyd | TEttinger, Yep.. |
| 05:53 | Olajyd | Thanks for taking out time to help out, TEttinger |
| 05:54 | TEttinger | no worries, it's always a good way for me to keep my clojure skills sharp. I haven't been using Clojure for anything major for a while, these refreshers for you and me help me remember the clojure I want to use for an upcoming thing |
| 06:02 | Olajyd | So out of curiorsity, is there any function that can force all prior processing to occur before moving on to the next function? |
| 06:14 | TEttinger | Olajyd, I believe there may be, but not for general processing. clojure has a number of tools like this... |
| 06:15 | TEttinger | there's futures, which you tell to start running as soon as they are declared, and if they have finished when you later request their return value, you get the return immediately, otherwise, you wait until the future finishes |
| 06:16 | TEttinger | the keyword is "blocking" |
| 06:16 | TEttinger | futures will block until they can return their final result |
| 06:20 | Olajyd | ok |
| 06:23 | Olajyd | Thanks TEttinger, so the reason I asked is because soon we’ll be structuring the code base to something like a pipeline, that for one function to run, a prior function mush have finished running |
| 06:24 | TEttinger | hm, sounds good. I know there are some things in clojure that are similar to futures, like delays, that you might want to look into |
| 06:25 | TEttinger | a step that must be completed to move on might be the body of a future, and you just dereference that future when you need it in the next pipeline step (which blocks until the previous step has a final value) |
| 06:26 | TEttinger | I don't know much about the performance of futures and there is likely a better way, though maybe not much better |
| 06:26 | Olajyd | uhmmm nice |
| 06:27 | Olajyd | will look into that |
| 06:29 | Olajyd | TEttinger, I saw this problem in stackoverflow, a function that takes rows: [[“1” “2” “3”] [“a” “b” “c”]] and produces: ([“1” “2” “3”] [“a” “b” “c”]) |
| 06:29 | Olajyd | is this possible? |
| 06:30 | TEttinger | just changing frm [] to () in the outermost? |
| 06:30 | TEttinger | ,(seq [["1" "2" "3"] ["a" "b" "c"]]) |
| 06:31 | clojurebot | (["1" "2" "3"] ["a" "b" "c"]) |
| 06:31 | TEttinger | ,(seq {["1" "2" "3"] ["a" "b" "c"]}) ; works on any collection |
| 06:31 | clojurebot | ([["1" "2" "3"] ["a" "b" "c"]]) |
| 06:31 | Olajyd | is it possible to have only [“1” “2” “3”] [“a” “b” “c”] without the brackets? |
| 06:31 | TEttinger | ah! |
| 06:31 | TEttinger | sort! |
| 06:31 | TEttinger | sorta! |
| 06:31 | Olajyd | heheh :D |
| 06:32 | TEttinger | so if you have this within another function, there's kinda a way |
| 06:32 | TEttinger | but there's no way to do multiple returns |
| 06:32 | Olajyd | hmmm great |
| 06:33 | TEttinger | ,`(map str ~@[["1" "2" "3"] ["a" "b" "c"]]) |
| 06:33 | clojurebot | (clojure.core/map clojure.core/str ["1" "2" "3"] ["a" "b" "c"]) |
| 06:33 | Olajyd | oddcully…deja vu? lol |
| 06:33 | TEttinger | which would need to be in a macro or be eval'ed |
| 06:33 | Olajyd | hmm |
| 06:33 | TEttinger | ,(apply map str [["1" "2" "3"] ["a" "b" "c"]]) |
| 06:33 | clojurebot | ("1a" "2b" "3c") |
| 06:33 | TEttinger | might be what you want |
| 06:34 | TEttinger | apply can remove the first layer of nesting from a collection |
| 06:36 | Olajyd | lol its a function assigned to my colleague though..I’m not very clear with his story on homw to implement it though..wanted to help out but..I’ll just set him up on IRC so he can ask himself |
| 06:37 | TEttinger | a clojure function can only return one value. that value could be 1, could be "hello", could be [[1 2 3] [:a :b :c]] |
| 06:39 | Olajyd | TEttinger, I’m sorry i dont understand what side effects mean in clojure functions :( |
| 06:39 | TEttinger | a clojure function can only return one value. that value could be 1, could be "hello", could be [[1 2 3] [:a :b :c]] |
| 06:40 | TEttinger | you can have side effects, which are things like changing an atom and then returning some other result unrelated to the atom |
| 06:40 | Olajyd | ok |
| 06:40 | TEttinger | one way to get two results out of a function is to have two side effects :) |
| 06:41 | Olajyd | ok |
| 06:41 | Olajyd | lol…nice, gotcha :) |
| 06:41 | TEttinger | ,[(def res1 (atom []))(def res2 (atom []))] |
| 06:41 | clojurebot | [#'sandbox/res1 #'sandbox/res2] |
| 06:42 | TEttinger | ,(defn side-effecting [n] (do (swap! res1 conj n) (swap! res1 conj (inc n)) (* n n n))) |
| 06:42 | clojurebot | #'sandbox/side-effecting |
| 06:42 | TEttinger | ,(side-effecting 1) |
| 06:42 | clojurebot | 1 |
| 06:42 | TEttinger | ,(side-effecting 2) |
| 06:42 | clojurebot | 8 |
| 06:42 | Olajyd | hmmm |
| 06:43 | TEttinger | ,[@res1 @res2] |
| 06:43 | clojurebot | [[1 2 2 3] []] |
| 06:43 | TEttinger | oh haha |
| 06:43 | TEttinger | I was adding to res1 both times |
| 06:43 | TEttinger | ,(defn side-effecting [n] (do (swap! res1 conj n) (swap! res2 conj (inc n)) (* n n n))) |
| 06:43 | clojurebot | #'sandbox/side-effecting |
| 06:43 | TEttinger | ,(side-effecting 20) |
| 06:43 | clojurebot | 8000 |
| 06:43 | TEttinger | ,[@res1 @res2] |
| 06:43 | clojurebot | [[1 2 2 3 20] [21]] |
| 06:43 | ane | is refactor-nrepl broken with clojure 1.7? |
| 06:44 | ane | travis builds seem to be failing |
| 06:44 | Olajyd | :) |
| 06:44 | Rurik | Why is swap! named swap! ? |
| 06:44 | TEttinger | the ! is so you know it isn't safe to call during a transaction IIRC |
| 06:44 | nooga | because oit swaps the value? |
| 06:45 | Rurik | nooga, I meant ! part |
| 06:45 | nooga | oh |
| 06:45 | TEttinger | I believe it's the transaction reason |
| 06:46 | Olajyd | yeaa hmm interesting what is the difference between the .swap and swap!? |
| 06:46 | TEttinger | I don't think there's a .swap that you're expected to use? |
| 06:46 | Olajyd | ok |
| 06:46 | TEttinger | that would be calling the java API which I guess you could do... but it wouldn't have a lot of clojure benefits |
| 06:47 | TEttinger | when I say java API |
| 06:47 | TEttinger | I meant the java that clojure for the JVM is coded with |
| 06:47 | TEttinger | and that's not the same as the standard java lib that clojure often should use, like for strings |
| 06:48 | Olajyd | gotcha |
| 07:02 | jaaqo | I think it read somewhere appending ! to a function name is to say this function has side-effects please be careful |
| 07:04 | ane | does evaluating the t/ann form of core.typed run the type checker? |
| 07:05 | ane | oh. no it does not. |
| 07:23 | Olajyd | I have a compare function to get the maximum value, (reduce #(if (pos? (compare % %2)) %1 %2) [[" " "2009/12/02"] ["4" "2005/02/08"] ["0" "2014/12/02"] ["5" "2005/08/01"] ["2" "2007/09/02"]]) ;=>["5" "2005/08/01”] but I want to go the comarison based on column 1, to give ;=> ["0" "2014/12/02"] |
| 07:24 | Olajyd | ,(reduce #(if (pos? (compare % %2)) %1 %2) [[" " "2009/12/02"] ["4" "2005/02/08"] ["0" "2014/12/02"] ["5" "2005/08/01"] ["2" "2007/09/02"]]) |
| 07:24 | clojurebot | ["5" "2005/08/01"] |
| 07:24 | tdammers | another selmer question: I need to iterate over a list of things, and use the current iteratee as an index into another thing |
| 07:24 | tdammers | something like: |
| 07:25 | tdammers | {% for item in items %}{{ descriptions[item] }}{% endfor %} |
| 07:25 | tdammers | except that Selmer doesn't have that kind of syntax |
| 07:25 | tdammers | I tried creating a "lookup" filter, such that I could write: |
| 07:25 | tdammers | {{ descriptions|lookup:item }} |
| 07:25 | tdammers | but it only works when I pass a string literal |
| 07:26 | tdammers | I can't seem to pass the second argument to that filter from an iteration variable |
| 07:26 | Olajyd | TEttinger, you there? |
| 07:27 | tdammers | oh crap, my custom filter gets passed the string "item" |
| 07:31 | tdammers | I think it's time to find a better template library |
| 07:32 | oddcully | Olajyd: don't compare the whole vector but only the second element |
| 07:37 | Olajyd | yeaa |
| 07:38 | Olajyd | oddcully any ideas on how to do that? |
| 07:38 | oddcully | destructuring, `second` |
| 07:39 | Olajyd | in the compare function? |
| 07:40 | oddcully | this is where you find out, what the smaller/larger item, right? |
| 07:40 | oddcully | s/what/&s/ |
| 09:28 | dstockton | is there a debugger that can work with vim/fireplace that anyone has had success with? |
| 09:28 | dstockton | i tried cider but i couldnt get it to play nicely with evil |
| 09:29 | ane | what problems did you have with evil and cider? |
| 09:42 | chomwitt | we can see in repl with ns-map changes we make to the current ns with refer. is there a command to do the same for require ? |
| 09:43 | justin_smith | chomwitt: ns-refers iirc |
| 09:45 | justin_smith | no, that's not it |
| 09:46 | justin_smith | chomwitt: ns-aliases works if you used :as when requiring |
| 09:47 | justin_smith | otherwise, just the fact that another ns required does not change your own ns (of course a refer changes ns-map) |
| 09:51 | visof | hi guys |
| 09:51 | visof | how can i do this haskell using clojure? take 10 (iterate (2*) 3) ? |
| 09:51 | dstockton | it just wasnt instrumenting right ane, i think the shortcuts are often conflicting |
| 09:51 | visof | [3,6,12,24,48,96,192,384,768,1536] |
| 09:51 | justin_smith | ,(take 10 (iterate (partial * 2) 3)) |
| 09:51 | clojurebot | (3 6 12 24 48 ...) |
| 09:52 | visof | justin_smith: what partial do? |
| 09:52 | dstockton | evil just makes me uneasy and i dont like the default emacs shortcuts |
| 09:52 | justin_smith | visof: it's a pathetic, weak, incomplete, disappointing substitute for currying |
| 09:53 | justin_smith | visof: but since we have varargs, it's pretty much the best we can get it seems :P |
| 09:53 | chouser | wow. I wasn't even sure automatic currying was a good idea. |
| 09:54 | justin_smith | chouser: I miss it so much :P |
| 09:54 | chouser | visof: (patial * 2) is basically the same as (fn [x] (* 2 x)) |
| 09:54 | chouser | justin_smith: from? |
| 09:54 | justin_smith | chouser: ocaml, and it's (fn [& args] (apply * 2 args)) |
| 09:55 | justin_smith | ,((partial * 2) 3 4) |
| 09:55 | clojurebot | 24 |
| 09:55 | chouser | yes, less "basically", but yes. :-) |
| 09:56 | visof | justin_smith: chouser thanks guys |
| 09:56 | visof | can i do composition of functions in clojure? |
| 09:56 | justin_smith | comp |
| 09:56 | justin_smith | ,((comp inc inc inc) 0) |
| 09:56 | clojurebot | 3 |
| 09:56 | visof | thanks |
| 09:58 | justin_smith | chouser: I only exaggerate my disappointment with partial because I've had people try to tell me partial was the same as currying (which of course it isn't) |
| 09:58 | chouser | righto |
| 09:58 | justin_smith | "I miss currying now that I do clojure" "but partial is currying" "..." |
| 09:59 | chouser | auto currying and succinct composition are nice for golfing in IRC, but the Clojure code I've been in for the last several months would I think have only been worse if auto currying were used |
| 10:00 | chouser | if (foo a b c d e f) invokes foo but (foo a b c d e) invokes nothing and returns a fn, slightly mystifying code would be even harder to comprehend. |
| 10:01 | justin_smith | chouser: yeah, currying was just an idiom I missed, but the idiom makes little sense without strong typing, and varargs are a nice tradeoff in the end |
| 10:01 | justin_smith | because yeah, with clojure's version of type enforcement the errors would be very hard to track down |
| 10:02 | chouser | yeah, I can see that static typing would help reduce the errors between the original code author and the computer |
| 10:02 | chouser | ...but subsequent human readers might still have unnecessary difficulty following along |
| 10:03 | chouser | I say "unnecessary" because partial, which is just as good as currying. |
| 10:03 | justin_smith | :P |
| 10:03 | justin_smith | I can almost accept "just as good", given the other trade offs, it's "is the same as" that sets me off |
| 10:04 | mungojelly | typing and testing both seem to me like basically the same strategy: have two versions of the code so they can check each other |
| 10:05 | justin_smith | mungojelly: they also act as documentation - making things explicit that can easily be ambiguous in clojure code |
| 10:06 | tdammers | the main difference is that tests are blacklists, types are whitelists |
| 10:06 | mungojelly | i really don't understand why we don't use any strategies that match more than two things? maybe because we're too lazy even to do two, usually. :/ |
| 10:06 | tdammers | (only really works when everything is typed though) |
| 10:07 | justin_smith | I wish I could get my team to do tests or data types. At least one of those. Hell, I'd settle for some assertions in the code at this point :P |
| 10:08 | Bronsa | puredanger: why is CLJ-1809 no longer in the 1.8 list? it's a serious regression caused by the direct linking changes |
| 10:11 | oddcully | justin_smith: lead from the front ;P |
| 10:12 | justin_smith | oddcully: I write tests and use schema. And then I end up needing to deal with accusations that I'm wasting my time doing so. |
| 10:12 | justin_smith | I'm not the lead |
| 10:16 | snowell | Getting management to look past RIGHT NOW and see the value of testing is way harder than it should be |
| 10:16 | mungojelly | does clojure have yet one of those cute things where you put a test in the form of a transcript in the doc string |
| 10:16 | justin_smith | you can attach a test as metadata to a function, in practice most of us prefer putting tests in their own namespace |
| 10:19 | tdammers | common problem: management prefers irrelevant quantifyable metrics over relevant unquantifyable metrics |
| 10:19 | Bronsa | (inc tdammers) |
| 10:19 | lazybot | ⇒ 1 |
| 10:20 | justin_smith | (inc tdammers) that's about it |
| 10:20 | justin_smith | (inc tdammers) |
| 10:20 | lazybot | ⇒ 2 |
| 10:20 | tdammers | I think this happens more often the deeper an organisation's hierarchy |
| 10:21 | tdammers | with deep hierarchies, almost all managers are somewhere in between the adjacent levels |
| 10:21 | tdammers | so they have to somehow make it such that whatever their subordinates do, they can produce quantifyable evidence that it wasn't their fault |
| 10:23 | mungojelly | conversely, is our intuition true that making coherent stuff that does what we want is what's important, lots of people seem to be profiting off code that just does some unpredictable something, maybe that's exciting |
| 10:26 | sobel | it is exciting |
| 10:26 | sobel | just..not in the good way |
| 10:28 | tdammers | something about local optima |
| 10:28 | justin_smith | mungojelly: in practice, without some kind of typing and/or testing and/or docs you end up with an unmaintainable system |
| 10:29 | justin_smith | nobody knows how to work on anyone else's part of the code base |
| 10:30 | justin_smith | you can't make large scale changes because too many things break, without any indicators of what your root causes of the bugs are, or where the real error is happening |
| 10:30 | mungojelly | i can accept that economic pressures make most code crap but also shouldn't there be some good code somewhere also, maybe just for fun, sigh |
| 10:30 | tdammers | there is plenty of good code in the world |
| 10:31 | mungojelly | justin_smith: but everybody knows that and they still tell us to make unmaintainable brittle systems, are they really getting it wrong or are supple systems more cost than they're worth |
| 10:32 | mungojelly | tdammers: can you recommend a project in clojure i should read? i need to read some good code that makes me feel better about the world |
| 10:32 | justin_smith | mungojelly: when the brittleness forces a full rewrite (which I have seen plenty of times) I take that as a sign that suppleness would have been worth it - building it supple once is easier than building from scratch twice |
| 10:33 | justin_smith | I've worked on plenty of clojure projects that wouldn't have happened if the ruby code that preceded it had been more supple |
| 10:34 | tdammers | mungojelly: haven't been doing enough clojure to suggest anything here, but there should be plenty |
| 10:35 | tdammers | mungojelly: I could recommend a bunch of Haskell projects... |
| 10:42 | mungojelly | i've been really annoyed as i've tried to learn to program by the absolute lack of simple interfaces to anything, it's very difficult to learn when nothing at all is simple, and i'm convinced there's no good reason at all |
| 10:43 | mungojelly | here's my first attempt at throwing a ladder back down to my fellow newbies: working title "7by7grid" it's an interface where what you pass around is lists of 49 RGBA colors |
| 10:44 | sobel | when i worked in a ruby shop i saw a lot of OO development |
| 10:45 | mungojelly | so then to make a piece that participates in that interface you just have to print out like 00000000 FFFFFFFF repeat for a while and it shows black and white stripes, etc., it goes right from print hello world to printing something pretty |
| 10:45 | sobel | they talked about FP but i only saw the clojure developer(s) doing it |
| 10:45 | visof | ,(take 10 (rest (iterate (partial * 2) 3))) |
| 10:45 | clojurebot | (6 12 24 48 96 ...) |
| 10:45 | visof | ,(rest (iterate (partial * 2) 3)) |
| 10:45 | clojurebot | (6 12 24 48 96 ...) |
| 10:46 | visof | (rest (iterate (partial * 2) 3)) guys this expression will return lazy too? |
| 10:46 | mungojelly | most of the functional programming i've seen is basically the same shape as OO anyway, instead of "objects" there's "data" and instead of mutation there's the "next version" of the data |
| 10:54 | puredanger | Bronsa: just ticket juggling for Rich |
| 10:55 | puredanger | He wanted 1805 and 1809 at the top of a list somewhere and this was easiest |
| 10:59 | sobel | mungojelly: sounds good to me. i regularly find pure functions much easier to debug and to compose. |
| 11:01 | mungojelly | sobel: yes they work better even if what you're writing in them is a translation of an OO feeling, you can more easily put time travel in because you're more um in control of the data. but so rarely do i feel like the programs i'm reading actually feel functional at heart. |
| 11:02 | mungojelly | like in a book about functional programming there's this beautiful ahhhhh quality to it, and then real life "functional" programs are always mostly really just poking slowly with sticks at piles of data |
| 11:04 | mungojelly | they have god objects, except they're smashed apart into a god pile of data and a god associated set of functions-- which is what an object is, if you think about it |
| 11:04 | sobel | ok, i have no idea what you're talking about or trying to do |
| 11:04 | sobel | i make things that work and it's easier in clojure |
| 11:05 | mungojelly | you'd think if you went to any even moderately large funcitonal program you'd find in there lots of awesome functions that do useful things, which you could then take out and compose into different programs, but no, it's a function like do-specific-thing-to-only-my-pile-of-data |
| 11:06 | sobel | o_O |
| 11:07 | sobel | i can't share your offense that application code is meant for one application, especially when it's built in clojure. the data structures make application core dev pretty easy already. |
| 11:08 | sobel | other languages need lots of (reusable) glue to make them modular, but with lisps you just get right to business because the language provides plenty of glue |
| 11:09 | mungojelly | obviously at some level you have your application specific code, but it would be nice if it were using parts that are as general as possible so everyone else could pick them up and do something else with them |
| 11:10 | sobel | have you perused clojars? |
| 11:10 | mungojelly | only a little so far! do you have any particular recommendations? |
| 11:11 | sobel | most of the community uses leiningen which resolves depedencies through maven repositories, clojars being the main community clojure module repo |
| 11:12 | mungojelly | yeah i've used leiningen to install a few things and clojars was where they came from |
| 11:12 | sobel | ok, so if you're looking for a library, search "<topic> clojars" and you should end up in a useful spot |
| 11:14 | sobel | that may be why you're not seeing library code in applications. |
| 11:28 | Leonidas | mungojelly: I kinda disagree that typing and testing is the same, because typing is not really a second version of your code. |
| 11:29 | mungojelly | Leonidas: yeah it's not another version of the same thing, but it's another related program, a similar but much vaguer program that just says which thingies can flow where |
| 11:30 | Leonidas | mungojelly: yeah, kind of. |
| 11:30 | Leonidas | the vagueness is depending on your type system, ranging from untyped to hmm, dependently typed. |
| 11:30 | mungojelly | are there any other things in that pattern? like, other versions of your code that you prop it up with. documentation, except if it's only written for humans no one checks it apparently. :/ |
| 11:31 | Leonidas | generative testing? |
| 11:33 | mungojelly | i've watched like three talks about generative testing now and it sounds cool but i haven't actually used it yet |
| 11:33 | Leonidas | depending on the problem it can be quite awesome. |
| 11:34 | mungojelly | yeah it doesn't actually fit the problem i'm working with. maybe i should make myself a better problem. |
| 11:36 | mungojelly | i like evolving code and there you get selection, which is similar to testing but less absolute, you can test a variety of things and score them on a curve rather than just pass/fail |
| 11:37 | sobel | i wouldn't like to use code that is tested on a curve |
| 11:37 | sobel | if i need a use case to work, i'd like its unit test to pass |
| 11:37 | deiodeiodeio | I'm using an external library with obscenely verbose logging (through clojure.tools.logging). How do I adjust its log level? |
| 11:38 | mungojelly | sobel: well it depends on what it's for, but you can do both, you can say absolutely fail if you don't pass these tests, also i grade you on how fast you did them |
| 11:39 | sobel | performance is pass-fail to me, too |
| 11:41 | sobel | that said, performance is often great, but i know where my risk areas are from experience |
| 11:41 | mungojelly | profiling you break it down and make variations in a quality and inquire, which one runs better. generally people seal it back up and put the "better" one as the one it does, done. another perspective is to keep both possibilities and let it find for itself which one is better, then it can adjust if the "better" path turns brittle. |
| 11:42 | deiodeiodeio | :( |
| 11:43 | mungojelly | i'm astonished by all this code that only knows one way to do each thing. of course it falls over often, there's no understudies for anyone. |
| 11:43 | sobel | deiodeiodeio: check the docs of the library you used |
| 11:43 | sobel | mungojelly: not sure what you're talking about. my database has a smart execution planner for queries but everything else doesn't need that kind of complexity. |
| 11:44 | deiodeiodeio | sobel: the library does not provide any facilities for setting log levels |
| 11:44 | sobel | deiodeiodeio: get a better one? |
| 11:44 | deiodeiodeio | er, there is just one |
| 11:44 | sobel | there's only one logging library for clojure? |
| 11:44 | deiodeiodeio | what, no, read what I wrote again |
| 11:45 | deiodeiodeio | the library I'm using uses clojure.tools.logging |
| 11:45 | deiodeiodeio | liberally |
| 11:45 | deiodeiodeio | and I want to shut it up so I can see my output |
| 11:45 | deiodeiodeio | preferably without writing any log4j files |
| 11:46 | snowell | I have a physical reaction to seeing log4j/slf4j anymore. No other library produces breaking API changes and incompatible versions like they do |
| 11:47 | deiodeiodeio | yeah me too |
| 11:47 | sobel | looks like i went with timbre |
| 11:48 | sobel | and it has a set-level! function that takes reasonable values like :info |
| 11:48 | deiodeiodeio | however, it doesn't seem like clojure.tools.logging exposes any methods of setting verbosity: https://clojure.github.io/tools.logging/ |
| 11:49 | deiodeiodeio | yeah, with timbre I would jut re-bind *config* and it would be fine |
| 11:49 | sobel | the only thing that threw me for a loop with it was it used a background logger, which required i call (shutdown-agents) to exit smoothly |
| 12:00 | deiodeiodeio | anyone? it seems like it shouldn't be hard, but I'm kind of stuck here |
| 12:00 | justin_smith | deiodeiodeio: clojure.tools.logging is just a frontend to the java logging stuff - it doesn't have anything for setting log levels, I think you'll need to make a .properties file |
| 12:01 | justin_smith | or a log.xml, or whatever |
| 12:02 | justin_smith | deiodeiodeio: the README for clojure.tools.logging has a 5 line log4j.properties you can copy/paste, the first line sets the output level |
| 12:02 | justin_smith | https://github.com/clojure/tools.logging |
| 12:03 | deiodeiodeio | justin_smith: yeah, thanks.. I would just rather not pollute my project with log4j files just because a library I'm using uses it |
| 12:04 | justin_smith | deiodeiodeio: they made that decision already - unless you know an alternative that doesn't use log4j, that's where you are at |
| 12:04 | deiodeiodeio | actually, (with-redefs [clojure.tools.logging/log (fn [& args])] (call-to-external-library)) seems to work :) |
| 12:04 | justin_smith | oh man that's evil |
| 12:06 | rhg135 | Isn't log usually a macro? |
| 12:07 | justin_smith | rhg135: in this case it is one, yeah https://github.com/clojure/tools.logging/blob/master/src/main/clojure/clojure/tools/logging.clj#L69 |
| 12:08 | justin_smith | now rebinding log* I could see working with with-redefs, but I am surprised rebinding log would work |
| 12:09 | deiodeiodeio | yeah, that would make it problematic |
| 12:09 | justin_smith | deiodeiodeio: log calls log* |
| 12:09 | justin_smith | but I still think configuring the lib is better than monkey punching it |
| 12:09 | deiodeiodeio | rebinding log worked it the repl.. rebinding log* seems to work regardless |
| 12:10 | rhg135 | Me too, justin_smith |
| 12:10 | deiodeiodeio | justin_smith: sure, but I refuse to give up now |
| 12:10 | justin_smith | be careful, monkeys throw poop if you get them mad enough |
| 12:11 | deiodeiodeio | hah |
| 12:11 | rhg135 | Monkey punch is a phrase I have not heard in this context |
| 12:11 | justin_smith | rhg135: it's a less innocuous rephrasing of "monkey patch" |
| 12:12 | justin_smith | rhg135: because you are doing something violent to the other code, not just altering it |
| 12:12 | mdeboard | What's the difference between "parking" and "blocking" wrt <! vs. <!! in core.async |
| 12:13 | justin_smith | mdeboard: whether the thread is allowed to do other work while your condition waits |
| 12:13 | mdeboard | Will it read from the channel when there is something available? |
| 12:13 | justin_smith | mdeboard: with parking, the thread that was running your code can be reallocated by core.async to do other work |
| 12:13 | rhg135 | Yeah, I'd say rebinding a macro name to a function is very bad |
| 12:13 | justin_smith | mdeboard: in both cases your thread resumes execution when data is available, the difference is what is done with your resources while waiting |
| 12:14 | mdeboard | Aha thanks justin_smith |
| 12:14 | mungojelly | is the convention here both macros and functions are spelled all lower case? is there some reason they should look the same? |
| 12:14 | justin_smith | mungojelly: upper case is for Classes |
| 12:14 | justin_smith | (not by restriction, but by convention) |
| 12:15 | justin_smith | mungojelly: in lisps functions and macros have always looked the same, as long as the two have existed |
| 12:16 | mungojelly | is it for a reason though? intuitively i feel like maybe it could be useful to macro-ify something that's already a function to change existing code, so i guess it's consistent with that |
| 12:16 | mdeboard | Sorry for this stupid question but I'd need to use like `(go-loop [] (do-stuff (<! my-channel)))` to read from the channel in a loop right? |
| 12:17 | justin_smith | mdeboard: well, for that to actually loop you would need a call to recur somewhere |
| 12:17 | mdeboard | (throw a `recur` in there) |
| 12:17 | mdeboard | yeah |
| 12:17 | mdeboard | lol |
| 12:17 | justin_smith | right, yeah, that is pretty much it |
| 12:18 | mdeboard | Ok cool |
| 12:19 | justin_smith | mungojelly: it's a convention dating to at least the '60s if not the '50s. I don't know if there is a lot of discussion since then even? I can see a rationale for wanting the difference between special-form, macro, function to be clear though |
| 12:19 | mungojelly | i wasn't born then but from what i've seen i seem to remember it being ALL CAPS ALL THE TIME at first for a while |
| 12:19 | sobel | from a tooling perspective i don't want them to be different |
| 12:19 | justin_smith | mungojelly: of course in C where macros have totally different compilation rules and can make whitespace changes totally break your code, UPPER_CASE_MACROS() are needed |
| 12:20 | justin_smith | mungojelly: right, they were case-insensetive, but there was no naming convention that separated special-forms, macros, and functions |
| 12:21 | justin_smith | mungojelly: I had generalized from "upper case" to "any sort of typographical distinction" |
| 12:25 | mungojelly | the early naming conventions in lisp do make it pretty clear they expected you to look up and memorize what every symbol means, nothing about them is guessable, they're not mostly even ordinary words |
| 12:25 | justin_smith | mungojelly: unlike C, lisp style macros don't have lexical gotchas (they don't transform code on a string manipulation level) and while you have to watch out for using them in invalid ways (eg. they are not functions you can pass to a higher order function, they bind values at compile time) the errors won't be nearly as bizarre if you get it wrong |
| 12:26 | mungojelly | justin_smith: i do understand that in theory but it slips my mind how macro hygiene actually works, does clojure like put a random number at the end of the real name of all your stuff? |
| 12:27 | justin_smith | mungojelly: we don't have actual hygeinic macros, but the ` (syntax-quote) reader-macro does namespace qualify symbols and forces you to use gensyms for new bindings |
| 12:27 | justin_smith | which in practice eliminates 98% of the accidental capture problems |
| 12:28 | mungojelly | oh hmm namespace qualify, that makes sense because i've noticed in clojure it's very consistent everything gets namespaced |
| 12:28 | justin_smith | '`(foo) |
| 12:28 | justin_smith | ,'`(foo) |
| 12:28 | clojurebot | (clojure.core/seq (clojure.core/concat (clojure.core/list (quote sandbox/foo)))) |
| 12:28 | justin_smith | it's verbose, but note how an explicit namespace was added |
| 12:28 | justin_smith | makes capture much less likely |
| 12:29 | mungojelly | ok i see, and that's a good-enough replacement for that actually ideal hygiene algorithm that's complicated enough to have slipped my mind |
| 12:30 | mungojelly | clojure in general seems very practical |
| 12:31 | justin_smith | mungojelly: I re-learned syntax-quote and syntax-case so many times and they still haven't stuck |
| 12:32 | justin_smith | (that is, the scheme version) |
| 12:32 | justin_smith | err s/syntax-quote/syntax-rules |
| 14:10 | mdeboard | Anyone have any guidance on why this function is not writing to a file but instead returning the `chan`? https://gist.github.com/mattdeboard/431ef638ba64848d247c |
| 14:11 | justin_smith | mdeboard: with-open closes the file on exit, go-loop exits immediately and does its thing in another thread, and returns a chan |
| 14:11 | justin_smith | s/file/writer |
| 14:11 | justin_smith | closes the writer on exit |
| 14:12 | mdeboard | I see, should I be opening the file inside the go-loop? |
| 14:12 | justin_smith | so what's happening is that the writer is being closed before the code in the go-loop even runs |
| 14:12 | mdeboard | Oh, weird. |
| 14:12 | mdeboard | ok |
| 14:12 | justin_smith | no, because then it would close before you run the next loop cycle |
| 14:12 | mdeboard | Can I pass the open file as a variable |
| 14:12 | mdeboard | in the `loop` call |
| 14:12 | justin_smith | and go loops don't work with the paradigm of with-open |
| 14:12 | mdeboard | `go-loop` |
| 14:12 | mdeboard | Ahhh |
| 14:12 | mdeboard | Ok. Huh. |
| 14:12 | mdeboard | So jus tmanually open/close |
| 14:12 | justin_smith | since the task can pass between multiple threads etc. |
| 14:12 | justin_smith | yeah |
| 14:13 | mdeboard | Mmkay |
| 14:13 | amalloy | justin_smith: i don't see why that's a problem. you can't use go-loop, but you can use go |
| 14:13 | amalloy | (go (with-open [...] (loop [...] ...))) |
| 14:13 | justin_smith | yeah, I would use lexical closure to access the writer, and then have a task that reads the return chan of the go-loop then immediatly closes and cleans up |
| 14:14 | justin_smith | hmm - and that works despite the parking and such that go is doing? |
| 14:14 | amalloy | of course. that's what go is for, making stuff look sequential |
| 14:14 | amalloy | with-open is just a let and a try/finally, and you know those work |
| 14:15 | justin_smith | amalloy: I guess I expected some weird interaction between the with-open block and the go block, but if they work together, that's handy |
| 14:15 | amalloy | it's not like the FileOutputStream or whatever cares what threads are writing to i |
| 14:15 | amalloy | t |
| 14:15 | mdeboard | Hm |
| 14:16 | amalloy | i mean, in fairness i have never tried any of this. but based on how i understand these things to work i would be fairly surprised to be wrong |
| 14:16 | mdeboard | amalloy, https://gist.github.com/mattdeboard/c566cf94a9e510cf86d6 doesn't seem tow ork |
| 14:16 | justin_smith | mdeboard: what exactly fails? |
| 14:16 | mdeboard | There's just no writing to the output file |
| 14:17 | amalloy | mdeboard: try something simpler. write some fixed characters to a file |
| 14:17 | justin_smith | mdeboard: there's no control flow in your code that would cause that loop to exit and thus let the with-open close the writer |
| 14:17 | justin_smith | mdeboard: instead it will sit there and wait for more messages, not letting the with-open shut down |
| 14:17 | mdeboard | ok yeah that works |
| 14:18 | justin_smith | so perhaps read until the channel is closed |
| 14:18 | justin_smith | or until you get a magic ::flush value |
| 14:18 | justin_smith | or whatever |
| 14:24 | amalloy | so that actually works, right? it writes things? |
| 14:24 | mdeboard | amalloy, Yep just was a bomb because I'm an idiot and didn't ever exit the loop -_- |
| 14:24 | mdeboard | In my defense I'm on a phone conference. |
| 14:24 | amalloy | okay that's fine. it's just i was trying to prove it to myself locally and never really use core.async so i am doing stuff wrong |
| 14:25 | amalloy | like i expected https://www.refheap.com/35a77d388881dec359d7174c9 to work but apparently it doesn't |
| 14:26 | justin_smith | amalloy: my concern is the try/catch of the with-open making any sense when you have parking inside |
| 14:26 | amalloy | justin_smith: go's rewriting code addresses try/catch i'm fairly sure |
| 14:26 | justin_smith | OK |
| 14:26 | amalloy | why would you worry about try/catch and not about, say, let or loop? |
| 14:26 | amalloy | those are all equally as squirrely |
| 14:26 | justin_smith | hmm... |
| 14:27 | amalloy | or even about function calls, like (println (<! ch)) |
| 14:27 | mdeboard | Sadly I use clojure so rarely nowadays I remember precious little :( |
| 14:28 | justin_smith | amalloy: I am familiar with let bodies that have multiple threads inside seeing their bindings, for example, but I have no existing model for a try/catch that jumps across thread boundaries |
| 14:28 | justin_smith | though I admit a park inside the binding vector is weird to me conceptually as well |
| 14:29 | amalloy | well, it't not really multiple threads, of course. it's one thread at a time, possible totaling more than one thread |
| 14:29 | justin_smith | right, but even the one at a time thing interacts oddly with my mental model of try/catch, though this is probably just exposing my ignorance |
| 15:16 | mdeboard | Gosh darnnit I do not understand D: |
| 15:19 | Bronsa | puredanger: wrt your tweet, do you want a ticket concerned with just making `satisfies?` faster or with moving the cache from the method level to the protocol level as I suggested as a possible solution? |
| 15:22 | uptown | does anyone know whether core.async pub/sub subscribers need to be explicitly unsubscribed from their topics or just close!d to remove them? |
| 15:22 | uptown | and, i suppose, whether publishers clean up internally afterwards? |
| 15:25 | uptown | there's no doc coverage anywhere that i can find |
| 15:30 | mdeboard | amalloy, justin_smith, turns out using an exception was being swallowed, presumably because of the goroutine, in my usage of `apply` |
| 15:31 | justin_smith | mdeboard: full discussion of that "swallowing" http://stuartsierra.com/2015/05/27/clojure-uncaught-exceptions |
| 15:31 | justin_smith | mdeboard: tl;dr: that's what java does by default for exceptions not in your main thread, you should explicitly use try/catch |
| 15:31 | mdeboard | Nice |
| 15:31 | mdeboard | Perfect, thanks. |
| 15:32 | mdeboard | Here's the working form https://gist.github.com/mattdeboard/c566cf94a9e510cf86d6 |
| 15:32 | mdeboard | original seen https://gist.github.com/mattdeboard/c566cf94a9e510cf86d6/revisions |
| 15:33 | justin_smith | mdeboard: nice, and the .flush is a good touch too |
| 15:33 | mdeboard | I learned it by watching you, alright? |
| 15:33 | mdeboard | but seriously you mentioned flushing and it made sense |
| 15:34 | justin_smith | ahh, right |
| 15:38 | mdeboard | Stupid quesiton probably |
| 15:38 | mdeboard | but calling bufferedWriter.flush() clears the buffer right? |
| 15:39 | justin_smith | I would expect that, but I don't have proof handy. |
| 15:41 | mdeboard | I'd expect it too but I'm getting some unexpected results |
| 15:42 | mdeboard | I don't think it does no |
| 15:45 | amalloy | justin_smith: well, by default clojure prints uncaught exceptions to the console |
| 15:45 | amalloy | er, java does |
| 15:45 | amalloy | it's *clojure* that makes those messages hard to find |
| 15:45 | justin_smith | amalloy: as the article says, in the main thread, not in other threads |
| 15:46 | justin_smith | I've had (especially core.async) threads just swallow expections with no printing, exactly as stuartsierra describes |
| 15:46 | justin_smith | *exceptions |
| 15:46 | amalloy | that has not been my experience in java, and i remember core.async at one point had a (try (do-your-stuff) (catch Exception e)) or something very like it |
| 15:46 | amalloy | but i could be remembering something wrong |
| 15:48 | justin_smith | amalloy: (do (.run (Thread. #(fn [] (Thread/sleep 10000) (/ 1 0)))) nil) ; won't work in a bot, of course |
| 15:48 | justin_smith | amalloy: that will print nothing |
| 15:49 | amalloy | justin_smith: for example, try (doto (Thread. #(throw (Exception. ""))) (.start)) |
| 15:49 | amalloy | it doesn't print anything in your repl buffer in emacs, but it *does* go to stdout, which is lein's process |
| 15:49 | justin_smith | ahh, my example above was broken |
| 15:50 | amalloy | (and you can see that if you look at the *swank* buffer, or *nrepl* or whatever) |
| 15:50 | justin_smith | yeah |
| 15:50 | amalloy | Exception in thread "Thread-11" java.lang.Exception: at user$eval2239$fn__2240.invoke(c3f4f6ddbaf4ac46782f4c04098f05de409d4881-init.clj:1) at clojure.lang.AFn.run(AFn.java:22) at java.lang.Thread.run(Thread.java:745) |
| 15:50 | justin_smith | I'm just using a terminal, you are right |
| 15:51 | amalloy | anyway, i think the default uncaught exception handler *is* to print to stderr, but a lot of clojure programs don't make it easy to see stderr |
| 15:51 | amalloy | so i don't really approve of blaming that on java |
| 15:53 | justin_smith | amalloy: yeah, I just double checked and I also get the stack trace in a vanilla java -jar repl |
| 15:53 | justin_smith | amalloy: so clearly something stuartsierra is blaming on java is actually happening in clojure land (maybe core.async related) because I have definitely seen the behavior he is talking about there... |
| 15:53 | amalloy | yes |
| 15:53 | xemdetia | its a shame the jvm doesn't give you some sort of namespace to output file pipe without some weird logging lib |
| 15:54 | justin_smith | xemdetia: you can do it with timbre (dunno how timbre rates on the weird scale for you though) |
| 15:56 | xemdetia | justin_smith, it does look good for new stuff but I have a lot of drunk ponies I am leading around with log4j already :( |
| 15:56 | xemdetia | I will have to make a note |
| 15:58 | amalloy | justin_smith: the catch in https://github.com/clojure/core.async/blob/master/src/main/clojure/clojure/core/async.clj#L885 is a fun one |
| 15:58 | amalloy | but probably not the issue in this particular case |
| 16:00 | justin_smith | amalloy: odd, I see the map function but no try at that link |
| 16:01 | amalloy | errr, scroll down a bit? it's right there |
| 16:02 | justin_smith | oh, duh |
| 16:02 | amalloy | https://github.com/clojure/core.async/blob/master/src/main/clojure/clojure/core/async.clj#L462 is another suspicious customer. if i do something equivalent to this uncaught business in my repl, nothing prints to the repl but only to lein's stderr |
| 16:02 | amalloy | (this is probably lein's fault, or nrepl's, rather than core.async's, since i don't think core.async can really do any better) |
| 16:03 | mdeboard | What's this syntax doing? `(let [{deletes :in-chan} (some-chan-generating-func)])` |
| 16:03 | mdeboard | specifically the map |
| 16:03 | mdeboard | as the first binding for let |
| 16:04 | mdeboard | Is it some kindof map-deconstructing bind? |
| 16:05 | mdeboard | destructuring bind* |
| 16:05 | justin_smith | mdeboard: destructuring |
| 16:05 | justin_smith | yes |
| 16:05 | justin_smith | ,(let [{a :a} {:a 42}] a) |
| 16:05 | clojurebot | 42 |
| 16:06 | justin_smith | ,(let [{{{c :c} :b} :a} {:a {:b {:c 42}}}] c) |
| 16:06 | clojurebot | 42 |
| 16:08 | justin_smith | ,(let [{{{[one two three] :c} :b} :a} {:a {:b {:c [41 42 43]}}}] two) |
| 16:08 | clojurebot | 42 |
| 16:08 | justin_smith | perhaps destructuring would be more pleasingly consistent if it grabbed items from sequences backwards |
| 16:15 | devn | With defrecords you can add arbitrary key value pairs |
| 16:16 | devn | How does this affect performance, if say you have 5 fields specified in the record definition |
| 16:16 | devn | and you add tack on an additional k/v pair |
| 16:16 | devn | where the field is not specified |
| 16:17 | justin_smith | devn: the fields defined for the record are still (incrementally) faster, unless you dissoc one of the defined keys, then you get a vanilla map back iirc |
| 16:17 | amalloy | devn: there's an extra implicit field named __extmap, which is just a map of all the non-basis keyvals specified for this instance |
| 16:17 | amalloy | do you? i thought you just couldn't do that |
| 16:17 | amalloy | ,(defrecord Foo [x]) |
| 16:17 | clojurebot | sandbox.Foo |
| 16:17 | amalloy | ,(dissoc (Foo. 1) :x) |
| 16:17 | clojurebot | {} |
| 16:18 | amalloy | ,(class (dissoc (Foo. 1) :x)) |
| 16:18 | clojurebot | clojure.lang.PersistentArrayMap |
| 16:18 | amalloy | huh. has it always been that way? |
| 16:18 | justin_smith | amalloy: as long as I remember |
| 16:18 | justin_smith | I recall Bronsa complaining about some annoying consequences of this for his project |
| 16:22 | Bronsa | amalloy: yeah it has |
| 16:22 | Bronsa | justin_smith: no, no, my issue was a bug with record literals, which is fixed in 1.8 |
| 16:23 | justin_smith | oh, cool |
| 16:23 | Bronsa | which I noticed yesterday actually caused a bug in scheme a while ago |
| 17:20 | kavkaz | I have a function which requires several functions I defined myself. I probably won't be reusing them elsewhere. Right now I have them defined with letfn. Is it better to define them with defn or just keep it this way and not have any of them in the namespace? |
| 17:21 | kavkaz | I read somewhere, perhaps in a book that you should use anonymous functions (or functions of that nature) within a function and not pollute the namespace |
| 17:21 | kavkaz | but I'm not sure if it's idiomatic to have long Clojure functions |
| 17:21 | justin_smith | kavkaz: I'd call that a style choice. |
| 17:22 | justin_smith | kavkaz: a compromise would be a top level letfn (though that would be weird), or use defn- to make private definitions |
| 17:22 | blake_ | Testing is easier if you let them pollute the namespace. |
| 17:24 | justin_smith | blake_: that's true, but I usually prefer the thing where tests cover what you expose publicly, rather than testing all the internals |
| 17:25 | justin_smith | then again lately I'd settle for tests existing at all :P |
| 17:25 | blake_ | justin_smith: That's--hah--probably a good clue, though, I think: If it's something you need to test, maybe it's not a good idea to bury it. |
| 17:26 | justin_smith | very good point |
| 17:27 | kavkaz | justin_smith: I see. |
| 17:27 | kavkaz | The thing is that the three functions are in a top-level letfn |
| 17:27 | justin_smith | kavkaz: another consideration is that fp and oo sometimes have different philosophies about what is hidden and why |
| 17:27 | kavkaz | I might as well just define them with defn |
| 17:27 | kavkaz | Ah I see |
| 17:28 | justin_smith | like in fp you want to be able to hide your implementation details so consumers don't break a data structure abstraction, but expose state |
| 17:29 | justin_smith | so it might be OK to expose functions (as long as they preserve your key data structure abstractions) |
| 17:29 | kavkaz | justin_smith: Interesting, I kind of get what you're saying. Can you give me an example? |
| 17:30 | justin_smith | kavkaz: if you implemented an immutable data structure, but had a setter method that mutated it for performance reasons, I would hide the setter |
| 17:31 | justin_smith | if you have a helper function that doesn't break your data abstraction, but simply implements part of your implementation logic, that's not as bad to expose |
| 17:31 | justin_smith | as long as that helper doesn't allow mutation of some sort, or breaking some other invariant your lib assumes / exposes |
| 17:31 | kavkaz | justin_smith: Ah I see, that makes sense now |
| 17:32 | kavkaz | Honestly these functions are just part of a large procedure. |
| 17:32 | justin_smith | kavkaz: given that we are using immutable data for most things in clojure, we don't have to be quite as careful about hiding things - the damage a misbehaving client can do is reduced |
| 17:32 | justin_smith | kavkaz: yeah, I figured, that's the normal thing in clojure code |
| 17:33 | blake_ | A big difference from OO to FP is that FP doesn't require as much encapsulation, since there's a philosophy of avoiding side-effects. |
| 17:33 | justin_smith | right |
| 17:33 | kavkaz | I've seen in a Clojure book before the author using multimethods and protocols, but I've never touched those two before |
| 17:33 | justin_smith | kavkaz: those become more helpful if you are writing a library where you want end users of the lib to provide concrete functionality |
| 17:33 | justin_smith | the kind of thing that gets called IOC or DI in OO |
| 17:34 | kavkaz | Oh I see. It's like abstract class in Java? |
| 17:35 | kavkaz | Protocols that is... sort of? |
| 17:35 | justin_smith | kavkaz: protocols are backed by java interfaces |
| 17:35 | hlolli | Justin Smith, just curious, are you not also a csound user? |
| 17:35 | justin_smith | or well, jvm interfaces, which are what also backs java interfaces |
| 17:35 | justin_smith | hlolli: yes I am, I use csound a lot (but not at work) |
| 17:35 | kavkaz | I mixed up abstract classess and interfaces |
| 17:35 | justin_smith | well, they are very similar in usage, yeah |
| 17:36 | justin_smith | multimethods are similar, but they allow more flexibility in dispatch |
| 17:36 | justin_smith | and "default implementations" which are similar to abstract class funcitonality in a way |
| 17:37 | hlolli | Ok, Im programming alot with csoung API in Clojure. Maybe you can help me with one thing, since I'm trying to live code with csound using clojure. I'm trying to find a way to make add-watch non-destrucitve. There is to say, if I put an errorous function in add-watch function, it will not fail the Agent and stop everything? |
| 17:37 | justin_smith | hlolli: so I assume you follow the csound mailing list? I've been active there on and off. |
| 17:37 | hlolli | Yes that's where this name rang a bell. |
| 17:37 | kavkaz | justin_smith: blake_ Thanks for your helps, I gotta go now |
| 17:37 | blake_ | kavkaz: NP |
| 17:38 | justin_smith | hlolli: have you checked agent-error and set-error-handler! |
| 17:38 | blake_ | Wow, csound, that takes me back. |
| 17:39 | justin_smith | hlolli: there's a few related things all listed together here - http://conj.io/ - if you check the section about agents |
| 17:39 | hlolli | nope, will do! |
| 17:39 | justin_smith | blake_: csound is still alive |
| 17:39 | blake_ | Yeah, but it's been years since I played with it. |
| 17:40 | justin_smith | blake_: it looks like there may be a new version of cecelia supporting csound again coming out before long |
| 17:41 | hlolli | nice, I will check it out. I've made some algorithms for live-coding but Im hoping to use more recursion. I think I also need to read more about multimethods, there is to say, the way a pattern would behave with different instruments. But that is for future task. |
| 17:41 | blake_ | justin_smith: Neat. |
| 17:41 | justin_smith | blake_: also, csound supports live instrument definition, and multi-core |
| 17:42 | justin_smith | (or, newer versions do that is) |
| 17:42 | blake_ | Gonna say, it's come a long way. |
| 17:56 | teoma | Where do asynchronous Quil exception messages go in Cider? I don't see them in the REPL or error buffers, but I've confirmed they appear when starting `lein repl' from the shell. |
| 17:56 | hlolli | Ok I looked into set-error-handler! and agent-error. Both of those will not prevent an agent failing, only saving last value without error in a failed agent. My failures are coming trough add-watch wathcers. So If there's a way that the watcher would not evaluate an errorous function in the first place, then it would never fail an agent. I'm thing about some catch/throw kind of way, but maybe validators could work too? |
| 17:58 | hlolli | Have you tried teoma, nREPL buffer, if you're using cider-jack-in it should be created in emacs. |
| 17:59 | teoma | hlolli: is that *nrepl-messages*? I don't see the error there... |
| 18:00 | hlolli | nrepl-server? Do you have that window? Im currently using shell to create lein repl and I use cider-connect so I may be wrong about the buffer name. |
| 18:01 | justin_smith | hlolli: what about putting a try/catch on your watching function? |
| 18:01 | justin_smith | err, inside |
| 18:02 | teoma | hlolli: *nrepl-messages* seems to be the only buffer with nrepl in its name. |
| 18:02 | hlolli | weird, are you using cider-jack-in? |
| 18:03 | teoma | hlolli: yes. |
| 18:04 | hlolli | Either you must have closed that buffer accedentaly, can try closing cider and restart it. Or there's some cider setting you have that prevents nrepl-server from being generated. Assuming you are using the moser recent version of cider and cider-nrepl. |
| 18:05 | hlolli | (dont know what happens when pasing code to irc) |
| 18:05 | hlolli | (defn event-register [instr p-name matrix pattern] |
| 18:05 | hlolli | ;[instr p-name & {:keys [on-tikk]}] |
| 18:05 | hlolli | (add-watch x-agent (keyword (apply str (name p-name) "-play")) |
| 18:05 | hlolli | (fn [k r old-state new-state] |
| 18:05 | hlolli | (let [on-tikk (event-calculator matrix pattern) |
| 18:05 | hlolli | mod-div (->> (map #(Math/ceil (/ (+ % 0.0001) 256)) on-tikk) |
| 18:06 | hlolli | (apply max) |
| 18:06 | hlolli | (* 256)) |
| 18:06 | hlolli | pat-len (count on-tikk) |
| 18:06 | hlolli | t-256 (mod (new-state :t-stack) mod-div) |
| 18:06 | hlolli | ] |
| 18:06 | hlolli | (if (not= (new-state :t-stack) (old-state :t-stack)) |
| 18:06 | hlolli | (if (some #(= t-256 %) (map double on-tikk)) |
| 18:06 | hlolli | (do (eval instr) |
| 18:06 | hlolli | |
| 18:06 | hlolli | (prn (mod (new-state :t-stack) 256)) |
| 18:06 | hlolli | ;(prn instr on-tikk "?") ;(prn k (new-state :bar) (new-state :section)) |
| 18:06 | hlolli | (send (eval (symbol "hlolli.clock" p-name)) (fn [p-map] (assoc p-map :arp-index (mod (inc (p-map :arp-index)) (p-map :arp-len))))) |
| 18:06 | hlolli | ))))))) |
| 18:06 | hlolli | |
| 18:06 | justin_smith | hlolli: please use refheap.com for code samples |
| 18:06 | hlolli | ok, sorry wont do that again. But since it's here, can you see a good place to set the watcher, justin? |
| 18:06 | hlolli | ok will do! sorry |
| 18:07 | teoma | hlolli: I have Cider 0.9.1. Maybe it's because nrepl-log-messages is nil (the default). |
| 18:08 | teoma | hlolli: Oh, actually it's not nil, though the default is nil. |
| 18:08 | sdegutis | Just stopped by to apologize for my recent behavior. I've been insufferable, arrogant, haughty, boastful, rude, disrespectful, and in general impolite. I'll do my best to not be such an annoyance anymore, and to be much more grateful for all your help. On that note, thanks for all your help everyone. |
| 18:09 | nooga | wow |
| 18:09 | hlolli | Did you configure ~/.lein/profiles.clj and set cider-nrepl version etc? |
| 18:09 | teoma | hlolli: yes, I have :plugins [[cider/cider-nrepl "0.9.1"]] there. |
| 18:10 | justin_smith | hlolli: you could surround the let block with a a try/catch that just prints the exception |
| 18:11 | hlolli | ok nice thanks Justin, will try that! |
| 18:11 | justin_smith | hlolli: also your eval could be a resolve call (and that would be much better) |
| 18:11 | justin_smith | err, the one wherey you have (send (eval ...)) that is |
| 18:11 | nooga | hlolli: what's that code for? |
| 18:13 | hlolli | It's a time-schedule function, takes in numbers that are triggered from 0 to 255 and the iteration speed of the counter that is modulo of 256 is adjusted by beats per minute. A way to quickly create musical timing patters. But this is one way of many possible. With less code I always loose freedome but gain coding speed. |
| 18:15 | nooga | hlolli: overtone huh? |
| 18:15 | hlolli | ok, will try resolve call. But this part of the function is just to update pattern info, so it iterates note index so I can make arpeggio patterns or scale patterns. |
| 18:15 | justin_smith | nooga: not overtone, overtone has at-at which is much simpler than this |
| 18:15 | justin_smith | in fact, you might find it helpful to look at at-at for a good example of scheduling recurring timed tasks |
| 18:16 | justin_smith | or even use at-at even if not using the rest of overtone :) |
| 18:16 | hlolli | I only use overtone for timing, there's to say, I have an impulse coming from overtone, that's all. All else is Csound based. |
| 18:16 | nooga | I discovered extempore recently |
| 18:17 | nooga | it's not clojure but looks interesting, fast enough to perform synthesis on the fly |
| 18:18 | hlolli | I tried metro and chnset for csound. But csound events are inaccurate from +/- 100ms while coming from overtone it's +/- 5ms, can't find a way to get this same accuracy from csound. But then again I know supercollider developers have been really progrimming a good accurate clock while csound development has too little been based on live-coding etc. |
| 18:18 | hlolli | well, "synthesis" itn't extempore all sample based? |
| 18:20 | justin_smith | hlolli: it supports sample-based dsp, but I don't know if it ships with anything interesting |
| 18:20 | justin_smith | surely not as complete a set of ugens as csound or even supercollider would give you |
| 18:22 | hlolli | Yes, extempore sounds good, at least from Andrew Soerensen, really liked his performance when I heard him. Also Im trying to hack with extempore since it has really good emacs multiplexer and also emacs-osc to have the letters dance with the beats. |
| 18:22 | nooga | hlolli: you can write a function that writes individual audio samples to dsp |
| 18:22 | justin_smith | it's like comparing ms paint to blender, because both can do pixels on a screen, and hey, if you implemented a few functions you could probably do what blender does in paint, right? |
| 18:23 | blake_ | No good. MS is closed source. |
| 18:23 | hlolli | yes, I will admit, samples sound much better, but I'm afraid that once I go samples I will get stuck there and learn little new about sound synthesis. |
| 18:23 | hlolli | But I ofcourse use samples alot. |
| 18:23 | nooga | http://benswift.me/2012/06/07/dsp-basics-in-extempore/ |
| 18:24 | justin_smith | blake_: OK tuxpaint and blender :P |
| 18:24 | nooga | these are not samples as in prerecorded sound clips |
| 18:24 | justin_smith | nooga: right, that shows you how to turn pixels on and off. Now make a convincing texture of hair on a 3d character. |
| 18:25 | nooga | well I think it's much more flexible than ugens if you want to write a synthesizer |
| 18:25 | justin_smith | nooga: that's right, it's individual DSP samples. It's still very low level and getting from that to what csound or supercollider provides is years of work. |
| 18:25 | nooga | or at least experiment with synthesis |
| 18:25 | nooga | well |
| 18:26 | hlolli | nooga, good question. I guess I could find out, I understand the basics of audio-buffers etc. But can't say I could do it now without internet. Audio programming book would be good source for this. But I guess javasound would be the tool to use. |
| 18:26 | blake_ | Which is cool, man. If that's what you're interested in. I still like to write DBMSes... |
| 18:26 | nooga | some day I'll build a modular synth FROM HARDWARE >:D |
| 18:27 | justin_smith | hlolli: steven yi (one of the csound devs) has pink, which is a clojure project that does dsp in pure clojure |
| 18:27 | justin_smith | hlolli: this might provide exactly what you want - though it is young, and doesn't have a lot - but it has a whole lot more than extempore does |
| 18:27 | hlolli | excacly, I've been following up on him alot. |
| 18:28 | justin_smith | nooga: I guess my complaint about the audio api that extempore provides is that it doesn't give me much I couldn't do myself in any language that can hook up to OS audio drivers. And if I'm going to be rolling that much on my own, why do I even need their language? |
| 18:29 | hlolli | csound is just such a good fully packed tool, and I know csound, so I've been too hesitant of learning pink and/or supercollider. But I did go deep into overtone. |
| 18:30 | nooga | justin_smith: don't know, but I'm sure Andrew had a blast writing it |
| 18:34 | teoma | I heard a Rich Hickey lecture where he said Overtone's scheduler suffered from problems that core.async could address. Just curious, do you know what he meant? |
| 18:44 | hlolli | No, problems I have been having when trying to prorgam schedulers is that when the state: time is on a specific state and should do event on that state, then the time state could live long enough for more than one event to shoot off. But it's not connected to at I think. |
| 18:47 | hlolli | Anyhov, Justin, this worked! The agent does not crash, but the "problem" now is that I need to find a way for clojure to store my function calls, or redesign it, so that it will continue calling the last function call that worked, but it's much better, even if one pattern fails, the other will continue. But for now a major bug has been solved. |
| 18:54 | justin_smith | hlolli: I bet you could do something interesting with core.async, and either repeatedly sending events to a channel, or recirculating them as they are handled back to the sending function for them to come again after a delay |
| 18:55 | justin_smith | hlolli: what you are doing now reads as a bit clumsy - it is using some very powerful tools that clojure provides where there are more nuanced (and less powerful) tools that could make this simpler |
| 18:57 | hlolli | yes, this is very clumsy for sure. Im learning as Im programming. I've been looking into core.async and for sure I will give that a try for learning experience. |
| 18:58 | hlolli | I did try atoms insted of agents and also some schedulers at stackoverflow, but so far using agents and watchers, I've been able to get the most accurate results. Don't know why, but I just experiment with lot of things and choose the best result. |
| 19:01 | hlolli | well, the force behind the clock is overtone, form there I have on-event function handler that send counted ticks from impulse to a numberpile that is stored in agent, from the agent I will control the events. These agents could be dismissed perhaps but I need some way of storing pattern info in a map etc. |
| 19:01 | justin_smith | hlolli: the way you are using the agent with watches, you could have a go-loop per instrument, and a pub/sub (a writer to a publishing channel replacing the agent, readers of subscriber channels replacing the watches) |
| 19:03 | hlolli | yes defenitely, I'm very new to core.async, but it shouldn't take a long time to switch over. I may need to rewrite the code anyway to make it more clear and easy to read. |
| 19:09 | hlolli | it's 1am in berlin, so I need to hit the bed. Hope to talk later Justin! Gute Nacht. |
| 19:13 | amalloy | i should start using that. "sorry guys, it's 1am in berlin. i gotta go" |
| 19:14 | gfredericks | ~g2g is <reply> It's 1am in berlin |
| 19:14 | clojurebot | Ik begrijp |
| 19:14 | gfredericks | ~g2g is <reply> pager just went off |
| 19:14 | clojurebot | Alles klar |
| 19:14 | gfredericks | ~g2g is <reply> realized haskell is better |
| 19:14 | clojurebot | Ack. Ack. |
| 19:15 | gfredericks | ~g2g is <reply> personal segfault |
| 19:15 | clojurebot | You don't have to tell me twice. |
| 19:16 | ferz_ | Hello gents, I crash my REPL when I try to bind input to symbol like so (def data (slurp *in*)) |
| 19:16 | sdegutis | When running some clojure.test tests via CIDER, and you get an error, is there a way to show what line triggered the error? |
| 19:16 | gfredericks | ferz_: shouldn't (slurp *in*) hang? |
| 19:16 | sdegutis | All it tells me is the name of the exception (NullPointerException). |
| 19:16 | ferz_ | Am I doing something wrong? I am able to get initial input but then it crashes with an exception after I run more commands |
| 19:16 | gfredericks | sdegutis: I'd check *e in the repl |
| 19:16 | sdegutis | gfredericks: oh cool never knew about *e thanks. |
| 19:17 | gfredericks | np |
| 19:17 | ferz_ | gfredericks: I think it does but I ended my input with ctrl-d , whats the standard way to get input and bind it to a symbol |
| 19:18 | gfredericks | ferz_: depends how much you want |
| 19:18 | sdegutis | Is a lexer or parser a good use-case for using transducers? |
| 19:18 | ferz_ | gfredericks: so I have to manually specify something? |
| 19:18 | sdegutis | It seems kind of like it's right up its alley, but I can't be sure. |
| 19:23 | ferz_ | I have 4 lines coming in from user input, how do I capture them / bind every word to one long vector for reuse? |
| 19:24 | sdegutis | ferz_: It's not clear what you're asking, what the context is. |
| 19:24 | ferz_ | sdegutis: that's the context hackerrank.com/challenges/diagonal-difference |
| 19:28 | sdegutis | Okay. |
| 19:30 | ferz_ | So whats the standard way to capture the input in a variable. |
| 19:31 | sdegutis | ferz_: Probably just read *in*. |
| 19:31 | sdegutis | ferz_: (slurp *in*) I'm guessing. |
| 19:31 | sdegutis | ,(slurp *in*) |
| 19:31 | clojurebot | "" |
| 19:32 | sdegutis | That should do it I bet. |
| 19:33 | ferz_ | right except this is what I get http://pastebin.com/UFm0SV3L |
| 19:34 | sdegutis | ferz_: It probably won't work inside a REPL due to the nature of a REPL hijacking *in* for your convenience. |
| 19:34 | sdegutis | ferz_: test it using normal standard-input procedures in a terminal. |
| 19:34 | ferz_ | oh so it's the REPL issue then? Technically it's not my code per se? |
| 19:36 | ferz_ | I know there is a (flush) function so I was thinking maybe I am forgetting it or something. |
| 19:38 | amalloy | if you slurp *in* you close *in* afterwards, and then the repl can't read new commands from you |
| 19:38 | amalloy | you can just slurp (string-reader "1 2 3 4") instead, and then later replace that with *in* when you're running for real |
| 19:39 | ferz_ | hm, I'll try that |
| 19:39 | sdegutis | ferz_: basically, write your code to take an input, and in the real code use *in*, and while testing use (string-reader "1 2 3 4\n") |
| 19:40 | sdegutis | ferz_: a normal function parameter will suffice for this |
| 19:40 | ferz_ | I'll try that, thanks |
| 19:43 | ferz_ | I know I probably sound retarded but I am new to Clojure and trying to learn it by solving some challenges online but they are very specific in their input and etc. it gets confusing with Clojure. |
| 19:48 | neoncontrails | ferz_: have a look around. There's lots of challenge sites, and I think they each attract a certain demographic |
| 19:49 | neoncontrails | My personal favorite is Project Euler. It's not Clojure specific, but you'll learn a lot implementing 10 solutions or so |
| 19:50 | ferz_ | I did some Euler before, it's pretty math heavy unfortunately |
| 19:52 | neoncontrails | There's usually a mathematical interpretation to those problems that can help you avoid brute-force labor, that's true |
| 19:53 | TEttinger | my favorite clojure problem is "make it short enough to do something unusual but also fit in one IRC message" |
| 19:54 | neoncontrails | I'm not sure that they're really "math problems," in the sense that what they really require is a bit of sophistication and cleverness with how you approach large numbers and number sequences that will blow your stack if you're not careful |
| 19:54 | TEttinger | I think of euler as math-heavy personally |
| 19:54 | ferz_ | yeah thats heavy CS/Math lol |
| 19:55 | TEttinger | I never got past pre-calculus in college |
| 19:55 | ferz_ | I tapped out at Calc 2 so I haven't studied anything beyond that |
| 19:56 | neoncontrails | Boo calculus. Hooray linear algebra |
| 19:56 | neoncontrails | (I never truly fancied math until LinAlg) |
| 19:56 | ferz_ | See, I didn't even get to that |
| 19:56 | ferz_ | I am sure I wouldn't mind other mathematics but Cal 2 (integration) got me good.. |
| 19:57 | ferz_ | I did well in Trig, Geometry, Algebra |
| 19:57 | neoncontrails | Consider taking it if you like computers. You'll find it a lot more relatable than calculus, certainly |
| 19:57 | ferz_ | I wouldn't be able to that's the thing. |
| 19:57 | ferz_ | I had to switch majors eventually |
| 19:58 | neoncontrails | Don't be so hard on yourself! Linear algebra doesn't necessarily depend on calculus. At some schools it's not even a prerequisite |
| 19:59 | ferz_ | I might watch online lectures, that's about it |
| 20:00 | neoncontrails | @ferz_: You might enjoy this essay. Strang's terrific -- and his lectures are online, free! http://www-math.mit.edu/~gs/papers/essay.pdf |
| 20:00 | ferz_ | it is a pre-req in my uni lol |
| 20:00 | ferz_ | I am mostly disillusioned by college anyway |
| 20:01 | ferz_ | had a teacher for "C programming" who didn't know about K&R. |
| 20:07 | neoncontrails | Can someone show me how to format this code to take an operator as input and apply it to the expression? http://pastebin.com/BzV4LA7Y |
| 20:08 | neoncontrails | This works for adding x and y, but I had to manually write in the +. (apply (:val @op) [x y)) doesn't work here |
| 20:09 | neoncontrails | Pretend I closed that vector |
| 20:10 | justin_smith | neoncontrails: you'd need [(:val @x) (:val @y)] |
| 20:10 | neoncontrails | Oh, right. Good call |
| 20:11 | justin_smith | ,(do (def op (atom +)) (def x (atom 12)) (def y (atom 30))) |
| 20:11 | clojurebot | #'sandbox/y |
| 20:12 | justin_smith | ,(apply @op [@x @y]) |
| 20:12 | clojurebot | 42 |
| 20:12 | neoncontrails | Outstanding! |
| 20:12 | amalloy | (quote + arg) seems super wrong |
| 20:13 | justin_smith | neoncontrails: of course you would also need to do the :val dereference for your version |
| 20:13 | amalloy | ,(quote + arg) |
| 20:13 | clojurebot | + |
| 20:13 | justin_smith | neoncontrails: though fyi you can put things other than hash-maps in atoms |
| 20:13 | justin_smith | (as above of course) |
| 20:14 | neoncontrails | justin_smith: Can you explain briefly what (do ...) is doing here? |
| 20:14 | justin_smith | ,(quote x y z a b c d e f g wat) |
| 20:14 | clojurebot | x |
| 20:15 | justin_smith | neoncontrails: it lets me make less screen scroll by putting three defs in one clojurebot command |
| 20:15 | neoncontrails | Haha, bravo. So it's not strictly necessary |
| 20:16 | justin_smith | ,(quote x (System/exit 0) (println "WAT")) |
| 20:16 | clojurebot | x |
| 20:24 | neoncontrails | justin_smith: Just to clarify your example and amalloy's, what's the issue with the way I used quote? |
| 20:25 | neoncontrails | I recognize that it's not correct, but I'm not sure I understand why |
| 20:27 | amalloy | quote receives a single expression, and protects that expression from evaluation: in (let [x 1] [x, (quote x)]), x evaluates to 1, but (quote x) evaluates to x |
| 20:27 | amalloy | giving quote more than one expression just makes no sense |
| 20:28 | hiredman | http://dev.clojure.org/jira/browse/CLJ-1282 |
| 20:28 | hiredman | the comments on that issue |
| 20:28 | neoncontrails | I see. I was hoping the quote might be a wrapper I could use to safely pass operators around without evaluating them |
| 20:29 | neoncontrails | But that was just a hope |
| 20:56 | mdeboard | thanks to amalloy and justin_smith I am now poised to reap sweet, sweet StackOverflow points http://stackoverflow.com/questions/23600387/how-do-you-write-to-a-log-file-in-clojure-using-core-async/32491340#32491340 |
| 20:56 | mdeboard | Look at me, I am the expert now |
| 20:58 | dbasch | mdeboard: that’s good, because when Stack Exchange goes public they’ll issue shares to users proportionally to their karma points |
| 20:58 | mdeboard | Sweeeeeeeeet! |
| 20:58 | mdeboard | I have a whole 430 |
| 20:59 | dbasch | mdeboard: I just made that up of course |
| 20:59 | mdeboard | That puts me in 95th percentile I'm sure |
| 20:59 | mdeboard | dbasch, Too late, you said it, You're liable |
| 21:00 | dbasch | having one upvoted answer probably puts you in the 95th percentile |
| 21:00 | mdeboard | (I do not give a rip about SO karma etc., I was being cheeky :)) |
| 21:01 | mdeboard | (I also do not actually know how SO works) |
| 21:06 | amalloy | mdeboard: you are actually in the top 4% of SO users |
| 21:07 | diminishedprime | If I'm writing a macro and want to name an argument "name" as in [name & rest], is there a way to do that without running into issues from clojure.core/name ? |
| 21:07 | mdeboard | wat. |
| 21:07 | mdeboard | I guess the bar is pretty low. |
| 21:07 | amalloy | yes, it is |
| 21:07 | mdeboard | and the user base is high |
| 21:07 | mdeboard | i.e. huffing spray paint |
| 21:07 | amalloy | or wait maybe i am doing bad math? |
| 21:07 | mdeboard | diminishedprime, I'd say don't call it 'name' or any other .. name that stomps stdlib functions. |
| 21:08 | mdeboard | -name, name- are easy alternatives |
| 21:08 | mdeboard | thing-name, etc., etc. |
| 21:08 | amalloy | http://stackoverflow.com/users/495154/mattdeboard claims top 68%, but http://stackexchange.com/leagues/1/alltime/stackoverflow/2008-07-31/495154#495154 makes it look like your rank is pretty high |
| 21:08 | mdeboard | ...leagues? |
| 21:08 | mdeboard | welp |
| 21:08 | amalloy | oh, that's all of stackexchange |
| 21:08 | mdeboard | *deletes account* |
| 21:10 | diminishedprime | mdeboard: I suppose that'd be the best solution. Is there a generally accepted practice for naming when you want to name something that's already in the stdlib? |
| 21:10 | diminishedprime | mdeboard: I.E. -name- vs name- |
| 21:11 | amalloy | it is good fun to call it name, and then later try to call clojure.core/name |
| 21:11 | amalloy | i don't know why you would want to rob yourself of that adventure |
| 21:11 | mdeboard | diminishedprime, I sure don't know. Local (to your codebase) consistency is more important than a language-wide stylebook (unless we're talking about Python) |
| 21:12 | mdeboard | Just generally you should avoid stomping built-ins :) |
| 21:13 | diminishedprime | mdeboard: Yeah, that makes sense. I didn't really think of what the overall effect would be to override "name" (or for that matter if it's even possible), but I see that if anything else in that namespace was using the stdlib name, then there'd be some serious issues. |
| 21:15 | mdeboard | well it would only matter inside that function body of course |
| 21:16 | diminishedprime | mdeboard: Yeah, but that body is potentially quite large, the macro system would be used for a dsl for some business folk. |
| 21:16 | mdeboard | i see |
| 21:19 | diminishedprime | mdeboard: Thanks a lot! |
| 21:20 | mdeboard | you're welcome |
| 21:40 | chomwitt | contrary to some tutorials i've read in the lein repl i can use (fully qualified) symbols from other ns , like clojure.string or ns defined by me. How can i check which namespaces have been required(loaded) in the current ns? |
| 21:40 | amalloy | chomwitt: namespace loading is global. if someone else loads a namespace, the stuff in it comes into existence and anyone can use it |
| 21:41 | amalloy | but you generally should specifically require anything you need, so that you don't rely on "oh this other file is loading the namespace i need" and then that stops working when namespace load order changes or whatever |
| 21:44 | chomwitt | amalloy: the strange to me is that none introductory material i've read is clear (to me at least) on this topic. |
| 21:44 | chomwitt | for example first time i learn that namespace loading is global (what ever that means exactly, cause i'm newbie in clojure) :-) |
| 21:46 | chomwitt | i've have an ebook in front of me now that says that in a repl in a ns i must require clojure.set.. but i use it with no require |
| 21:46 | justin_smith | chomwitt: well, some code somewhere alreayd required it |
| 21:46 | chomwitt | except if that require happens one time per session and i've forgot it |
| 21:47 | amalloy | chomwitt: there are a lot of things you "must" do, like look both ways before crossing the street, but there's no law of physics that forces you to |
| 21:47 | amalloy | most of the time, no car hits you |
| 21:47 | amalloy | but it's still something you should do |
| 21:48 | chomwitt | justin_smith: should i reboot my pc to reset and test a fresh repl so that nothing would have already 'required clojure.set or whatever? |
| 21:48 | amalloy | justin_smith: clojure.core requires clojure.set and clojure.string |
| 21:49 | justin_smith | chomwitt: no, that's not really needed |
| 21:49 | justin_smith | amalloy: oh, I wasn't aware |
| 21:50 | justin_smith | amalloy: in a virgin java -jar repl, clojure.set/union gives me a ClassNotFoundException for clojure.set |
| 21:50 | justin_smith | :P |
| 21:50 | justin_smith | I wouldn't be surprised that every other lib ever written uses both those though |
| 21:51 | chomwitt | so maybe 'lein repl' does some things differently.. |
| 21:51 | TEttinger | I rarely use clojure.set for some reason |
| 21:51 | justin_smith | oh, it definitely does |
| 21:51 | emdashcomma | Yesterday I was facing an awkward REPL situation |
| 21:51 | emdashcomma | I put the code at the end of Ch. 4 of Brave Clojure in a file and then opened a REPL -- I got a lot of runtime exceptions |
| 21:51 | chomwitt | justin_smith: was that line related to my last one? |
| 21:52 | emdashcomma | I then typed all of it into a REPL outside of that project, and it worked fine -- what's going on? |
| 21:52 | justin_smith | chomwitt: yes - lein repl uses nrepl, that loads a bunch of things that are not present in a "vanilla" clojure.jar repl |
| 21:52 | chomwitt | justin_smith: thanks |
| 21:52 | justin_smith | emdashcomma: which functions were causing the errors |
| 21:53 | emdashcomma | Let me get the exact line |
| 21:54 | chomwitt | i think i'd help me to have a command like 'ns-map' to check the status of loaded (required?) ns in my current one. now i probe with TAB to see what's loaded. |
| 21:55 | emdashcomma | > Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: create in this context |
| 21:55 | emdashcomma | 'create' is nowhere in the code, though |
| 21:55 | justin_smith | chomwitt: all-ns |
| 21:55 | amalloy | justin_smith: what about clojure.string? |
| 21:56 | justin_smith | amalloy: that one is loaded |
| 21:56 | amalloy | probably clojure.repl loads string then, not clojure.core |
| 21:57 | justin_smith | amalloy: nope, clojure.repl is loaded here https://www.refheap.com/109368 |
| 21:57 | chomwitt | justin_smith: thanks again . |
| 21:58 | justin_smith | chomwitt: here is a fun one: (into {} (map ns-publics (all-ns))) |
| 21:58 | amalloy | right, i said string, not set. i wasn't disagreeing with you |
| 21:59 | amalloy | i was clearly wrong about clojure.set, and also concluding that it's probably not clojure.core that loads clojure.string, but clojure.repl |
| 21:59 | alif-baa | emdashcomma: and what code were you running |
| 22:00 | chomwitt | justin_smith: thats a lot output do digest ! |
| 22:01 | emdashcomma | http://pastebin.com/PtFpNvrj |
| 22:03 | justin_smith | chomwitt: see also clojure.repl/apropos |
| 22:03 | justin_smith | ,(clojure.repl/apropos "namespace") |
| 22:03 | clojurebot | (clojure.core/namespace clojure.core/namespace-munge) |
| 22:04 | justin_smith | ,(clojure.repl/apropos-doc "namespace") |
| 22:04 | clojurebot | #error {\n :cause "No such var: clojure.repl/apropos-doc"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.RuntimeException: No such var: clojure.repl/apropos-doc, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyze "Compiler.java" 6704]}\n {:type java.lang.RuntimeException\n :message "No such var: clojure.repl/apropos-doc"\n :at [clojure.lan... |
| 22:04 | justin_smith | ,(clojure.repl/apropos "apropos") |
| 22:04 | clojurebot | (clojure.repl/apropos) |
| 22:04 | justin_smith | hmm, must be an extension I forget where from |
| 22:08 | justin_smith | ,(clojure.repl/find-doc "namespace") |
| 22:08 | clojurebot | -------------------------\nclojure.core/*data-readers*\n Map from reader tag symbols to data reader Vars.\n\n When Clojure starts, it searches for files named 'data_readers.clj'\n at the root of the classpath. Each such file must contain a literal\n map of symbols, like this:\n\n {foo/bar my.project.foo/bar\n foo/baz my.project/baz}\n\n The first symbol in each pair is a tag that w... |
| 22:08 | justin_smith | that's the one, repl/find-doc, searches for a string and regex in clojure doc strings |