2014-05-04
| 00:35 | eg0 | anybody familiar with light table? cant save anything, get eacces permission denied |
| 00:36 | eg0 | even in user directories |
| 00:37 | eg0 | does it need to run as root? |
| 01:03 | TEttinger | eg0: that's awfully odd |
| 01:03 | TEttinger | on windows? |
| 01:04 | eg0 | TEttinger: ubuntu |
| 01:04 | eg0 | reinstalling everything now |
| 01:16 | itimmy | Allow me to introduce myself, my name is itimmy and I was sent here from the gods |
| 01:26 | satshabad | I want something like an some-> except that in adition to a nil, I get an error message back too |
| 01:26 | satshabad | addition* |
| 01:26 | satshabad | I... I think I want a Monad? |
| 01:27 | satshabad | Can I use maybe ..half a monad or something? |
| 01:27 | amalloy | satshabad: yeah, you kinda want Either. but hey, exceptions have been the special-case monad for that in java for years |
| 01:28 | satshabad | try catch? hmm, I was kind of hoping for elegant tonight |
| 01:29 | satshabad | Maybe something exists that can help for ring/compjure? I want to thread a response through functions and if one of them fails I need a status code and a message |
| 01:30 | satshabad | this is interesting: http://adambard.com/blog/acceptable-error-handling-in-clojure/ |
| 01:40 | amalloy | satshabad: that's literally just a hand-rolled implementation of Either |
| 01:40 | satshabad | haha I know :) |
| 01:40 | amalloy | which, okay, Either is pretty cool. but if you wanted to avoid monads... |
| 01:41 | satshabad | I am settling for making a macro called if-not-let and nesting them because I made myself promise I would get some stuff done tonight |
| 01:42 | eg0 | TEttinger: got things going |
| 01:42 | TEttinger | that's good |
| 01:42 | TEttinger | what do you think caused it, eg0? |
| 01:57 | Morgawr | Are there any resources or guides or books or something that go deep into the implementation of Clojure as a hosted language? Like explanations of the source code, differences between clojure and clojurescript implementations, other clojure dialects, etc etc? |
| 02:06 | TEttinger | Morgawr, that would be nice wouldn't it... I don't think it exists right now |
| 02:06 | Morgawr | TEttinger: a shame :( |
| 02:07 | TEttinger | there might be a read-through of core.clj somewhere |
| 02:07 | Morgawr | I would love for somebody to create a Clojure dialect that runs on rust |
| 02:07 | TEttinger | but really what you're interested in is the Java side, or JS side? |
| 02:07 | TEttinger | or LLVM! |
| 02:07 | Morgawr | rust is on llvm |
| 02:07 | Morgawr | so yeah |
| 02:08 | Morgawr | I'm interested in the process of building a clojure language that runs on <whatever platform> |
| 02:08 | Morgawr | like, I know there are very big differences between clojure and clojurescript when it comes to the implementation, mostly because Clojurescript is translated into javascript and Clojure is just compiled into bytecode (and other nifty stuff) |
| 02:09 | Morgawr | but I don't know much about either, I've been browsing the source code (the .java files mostly) but I probably need more time cause most of it went over my head |
| 02:09 | TEttinger | people seem to be implementing kernel a lot |
| 02:09 | TEttinger | http://axisofeval.blogspot.com/2011/09/kernel-underground.html |
| 02:10 | TEttinger | lisp-like language |
| 02:10 | Morgawr | TEttinger: nice |
| 02:11 | TEttinger | might be a good place to start, Wat was 400 lines of JS at one point IIRC |
| 02:11 | TEttinger | (and could eval at that point) |
| 02:12 | Morgawr | https://github.com/brandonbloom/eclj this seems interesting |
| 02:14 | TEttinger | woah |
| 02:15 | TEttinger | bbloom, that's slick |
| 02:15 | Morgawr | oh, it's from bbloom |
| 02:15 | Morgawr | neat |
| 02:17 | Morgawr | http://www.infoq.com/presentations/Clojure-LLVM/ this is also probably worth watching |
| 02:18 | eg0 | oooo thats awesome |
| 03:39 | ivan | is there a way to keep track of aliases in the user namespace and ns-unalias+alias them after a tools.namespace refresh? |
| 03:49 | ivan | oh ns-aliases |
| 03:52 | quizdr | any clojurescript users in here at the moment? I have a question on compiling into the js file |
| 04:00 | Morgawr | quizdr: just ask your question and people might answer :) |
| 04:06 | quizdr | Well, I'm trying to understand why a single line of clojure, a defn, in my cljs file compiles out to an extremely long js file, when the only optimization I have is "whitespace". What exactly is all that? Secondly -- is it necessary to use the lein command for compiling, rather than a simple Ctrl-C,Ctrl-K in emacs as with Clojure? |
| 04:08 | Morgawr | quizdr: for your first question, as far as I understand clojurescript needs to provide most of the clojure runtime (stdlib, etc etc) inside javascript. So effectively you're not just compiling that defn into javascript, you're compiling the whole clojure library into javascript |
| 04:09 | Morgawr | and if you don't have optimiations (google closure's pruning, etc etc) it's going to be quite lengthy of course |
| 04:09 | Morgawr | and for the other question, I don't use emacs so I don't know what Ctrl-C, Ctrl-K does but I usually use lein autobuild (or just build) to compile clojurescript stuff in the background while I work (I use vim and lighttable) |
| 04:10 | Morgawr | there is a clojurescript compiler if I remember correctly but everything is abstracted away thanks to lein so I don't know more |
| 04:10 | quizdr | interesting, so my js file actually contains everything needed for all the core clojure functions to work in an independent JS environment? that's pretty fascinating. |
| 04:11 | Morgawr | I might be wrong but that's how I take it, yes, and if you aren't optimizing (aka removing unwanted/unused code), that's what you get |
| 04:11 | TEttinger | quizdr, it is in java too. that's what the dependency on clojure.jar is |
| 04:11 | Morgawr | but you might want to investigate further (or wait for somebody else to clarify) because I'm not an expert in clojurescript (nor clojure for that matter) |
| 04:11 | TEttinger | (clojure.jar has all of the stuff in core.clj and related ns's) |
| 04:12 | TEttinger | it might be good to investigate what's in the jar that lein downloads, Morgawr |
| 04:13 | quizdr | What exaclty is a simple "println" doing in clojurescript? In clojure, it defaults to the REPL's standard output. But the JS code does not appear to by default do somethign like console.log() in JS |
| 04:13 | Morgawr | TEttinger: definitely |
| 04:13 | Morgawr | I mean, I know about clojure.jar, I just wasn't sure about javascript |
| 04:14 | Morgawr | quizdr: http://stackoverflow.com/questions/19963948/how-to-set-the-clojurescript-print-fn-fn-in-nodejs this might be useful |
| 04:14 | Morgawr | I usually use (.log js/console ...) in clojurescript |
| 04:14 | ptcek | Is there any way to define records with defaults (other than macros like http://cemerick.com/2010/08/02/defrecord-slot-defaults/) |
| 04:15 | TEttinger | Morgawr: yep, the jar that lein uses has not only clojure.core, but also several other ns's I didn't know came with |
| 04:16 | TEttinger | pprint, zip, xml |
| 04:16 | guns | ptcek: I think a custom ->Record constructor is the best answer. A little verbose but explicit |
| 04:17 | Morgawr | I usually write a myrecord-init function for all my defrecords |
| 04:17 | Morgawr | that macro linked might turn out useful (but I dislike the name) |
| 04:18 | guns | It's sometimes useful to create records with implicit fields (like timestamps). that's just more macro magic of course |
| 04:19 | Morgawr | on a slightly related note, I was benchmarking some code and it seems that calling a record's constructor multiple times in performance-critical code is faster than just using assoc on an already instantiated record, does this make sense? |
| 04:19 | Morgawr | and I mean, I got a critical boost in performance from such a change in my code |
| 04:19 | guns | what kind of % delta? |
| 04:19 | Morgawr | original code ran in 12seconds, with the record re-instantiation it ran in 6-7 seconds |
| 04:20 | Morgawr | so, cut down the run time in half |
| 04:20 | guns | pretty big. I guess the ctor doesn't even bother with the IPersistentMap interface |
| 04:20 | guns | just a guess |
| 04:20 | Morgawr | I have a triple defrecord to hold coordinates x,y,z and changed (assoc old-inst :x 0 :y 0 :z 0) to (Triple. 0 0 0) (obv with real values) |
| 04:21 | guns | oh yeah, that should be faster |
| 04:21 | Morgawr | good to know :) |
| 04:21 | guns | assoc with multiple values just does a simple reduce |
| 04:21 | Morgawr | ah |
| 04:21 | Morgawr | so it's building several layers? |
| 04:21 | guns | it does the persistent coll thing |
| 04:22 | quizdr | So I guess of a JS file contains all of clojurescript's implementation, this assumes that most applications of clojurescript will just result in a single JS file, as otherwise you'd have a massive duplication of the core cojurescript libary in every Js file |
| 04:23 | afhammad | given a vector of maps with :id's what is best/fastest way to insert a new map after map with :id x |
| 04:23 | afhammad | I realize the vectors might not be best suited for this operation, open to suggestions |
| 04:24 | Morgawr | quizdr: not necessarily, if you are building a clojurescript project with dependencies, they are all compiled together and then optimized away with google closure compiler (which removes unused code if you decide so, and minifies everything) |
| 04:24 | Morgawr | afhammad: why not use a map of maps and store the id as key? |
| 04:25 | Morgawr | like { 0 { :id 0 :value "whatever" } 1 { :id 1 :value "whatever2"} } etc etc (might even remove the :id field at this point if you want to) |
| 04:26 | guns | And if all the :id values are positive ints, you can use the new immutable-int-map library for performance |
| 04:26 | Morgawr | guns: oh, never heard of that, got any resources? it might turn out to be useful for one of my projects |
| 04:27 | guns | Morgawr: https://github.com/ztellman/immutable-int-map |
| 04:27 | guns | It screams "build a trie" |
| 04:28 | Morgawr | aw, it uses parallel threads.. cool but it's not going to work for me |
| 04:28 | afhammad | FYI this is in ClojureScript |
| 04:28 | Morgawr | still an interesting library |
| 04:28 | Morgawr | afhammad: shouldn't matter |
| 04:29 | guns | Morgawr: It only mentions parallel threads in the discussion about the reducers library |
| 04:29 | guns | it is totally concurrency agnostic |
| 04:29 | Morgawr | ah |
| 04:29 | Morgawr | so it doesn't use threads in the implementation, good good |
| 04:29 | Morgawr | I misunderstood the readme then |
| 04:29 | afhammad | also I need to traverse the collection sequentially as it is being rendered as dom elements |
| 04:30 | guns | it just so happens to be very good at merging |
| 04:30 | Morgawr | afhammad: you can get a list out of a map using the vals function |
| 04:30 | Morgawr | not sure if it's faster/slower though |
| 04:31 | afhammad | ok, will look into it. Thanks guys |
| 04:36 | Morgawr | https://code.google.com/p/jvmtop/ just found out about this, pretty cool tool |
| 04:36 | Morgawr | thought it'd share |
| 04:38 | guns | Morgawr: oh that is useful! It's nicer than htop showing a 100 "java" threads |
| 04:38 | Morgawr | guns: yeah, exactly |
| 05:13 | yotsov | is there a tool that allows to have a REPL running in a web app? server-side I mean? Does ring or http-kit provide this functionality? |
| 05:30 | martinklepsch | hey. I'm working on parsing patents from the USPTO for some time now and have been using zippers so far tog get relevant elements. but as I need more and more data/work with various versions of those files the zipper api seems limiting |
| 05:30 | martinklepsch | https://www.refheap.com/85084 |
| 05:31 | martinklepsch | I wonder if an easier way might be to write a function that converts this whole thing to a hash map (omitting attributes) |
| 05:32 | martinklepsch | (just noticing that this wouldn't work with <b><a>hello</a><a>world</a></b>) |
| 05:36 | TEttinger | martinklepsch, I can see why you'd want to parse that patent in particular, that's hilarious |
| 05:36 | martinklepsch | TEttinger, novelty jeans for the win! |
| 05:37 | TEttinger | so you need to parse malformed HTML? |
| 05:37 | TEttinger | err XML |
| 05:38 | TEttinger | I guess that example wasn't malformed |
| 05:39 | TEttinger | so what kind of data are you trying to get out of it, martinklepsch? |
| 05:40 | martinklepsch | TEttinger, yeah. already doing that using xml-zippers but the API feels clumsy (maybe because I didn't understand it well enough too) |
| 05:40 | Morgawr | I've been reading Clojure High Performance Programming and wow it is a good book |
| 05:41 | Morgawr | I strongly recommend it |
| 05:41 | martinklepsch | TEttinger, right now I want to build a UID by the stuff thats in the document-id tag |
| 05:41 | TEttinger | I uh... wouldn't expect to hear that that book exists |
| 05:42 | Morgawr | TEttinger: it digs deep into the details of the JVM, how to write fast clojure applications, how to leverage the JVM and its advantage, parameters to pass to the jvm, comparisons for various clojure structures and operations, etc etc |
| 05:42 | Morgawr | really a good read |
| 05:42 | Morgawr | and it's also relatively short too, only 150ish pages |
| 05:42 | TEttinger | Morgawr, will you do the hard work and optimize Cloister for users like maybe me? |
| 05:43 | Morgawr | TEttinger: yeah, this is part of why I'm reading this book :) |
| 05:43 | Morgawr | I'm currently doing some work at uni (hopefully publish something, fingers crossed) on performance comparisons of java and clojure in multithreaded environments so this book is also useful for that |
| 05:44 | Morgawr | and with the knowledge I gain I hope I can work more on Cloister during the summer |
| 05:44 | TEttinger | martinklepsch: honestly the simplest possible way I can think of is to hash the contents of that document-id |
| 05:44 | TEttinger | (might want to not use a JVM hash function, use your own) |
| 05:50 | TEttinger | neat, clojure MD5 that shouldn't change on JVM updates. http://zackmdavis.net/blog/2014/01/consistent-hashing/ |
| 05:51 | xecycle | Does the repl completion support Java-only classes? |
| 06:00 | martinklepsch | TEttinger, this would probably help with the UID but it doesn't really help with similar issues in other contexts where I actually need the data |
| 06:00 | TEttinger | indeed... |
| 06:02 | penthief | cider-jack-in (v0.6.0alpha) doesn't seem to use :repl-options like "lein repl" does. Am I missing something? |
| 06:26 | martinklepsch | parsing XML files I now a couple of times wondered about how I could maintain path information to specifc nodes w/o listing the complete path for each node... https://www.refheap.com/85085 |
| 06:30 | martinklepsch | TEttinger, ^ somewhat related to my previous question, if you're curious |
| 06:30 | TEttinger | I actually have no idea how to use the clojure zippers, sorry |
| 06:34 | martinklepsch | TEttinger, has nothing to do with clojure zippers actually |
| 06:35 | Kneiva | updating my ring/compojure project to use clojure 1.6.0 (was 1.5.1) gives me this when running lein ring server-headless: https://www.refheap.com/85086 |
| 06:38 | Kneiva | any idea what's wrong? |
| 06:39 | martinklepsch | Kneiva, seems like a dependency issue? |
| 06:39 | martinklepsch | http://www.slf4j.org/codes.html#StaticLoggerBinder |
| 06:39 | Morgawr | question: if I create an agent inside a transaction and I send some computation to run (from inside the transaction), I know that the send will be queued until the transaction is committed, but if it's replayed, it will create a new agent, is there a risk to send the same computation twice to two different agents? |
| 06:39 | Kneiva | that has been there before |
| 06:40 | Morgawr | as in, are agent creations also buffered inside a transaction? |
| 06:40 | Kneiva | martinklepsch: that ns_tracker stackoverflow is the new thing |
| 06:43 | AeroNotix | Morgawr: try it? |
| 06:44 | Morgawr | AeroNotix: I don't know how, I've tried with a small concurrent test running pmap but it's hard to verify, do you have any suggestion on a piece of code that would try this? |
| 06:46 | AeroNotix | Morgawr: use a ref with two transactions, one transaction completes quickly (e.g. 50ms) and the other slow (e.g. 500ms) |
| 06:46 | Morgawr | I tried this |
| 06:46 | Morgawr | (dorun (pmap (fn [_] (dosync (alter r inc) (let [b (agent 0)] (send b (fn [_] (swap! x inc)))))) (range 1 10000))) |
| 06:46 | Morgawr | where r is a ref and x is an atom |
| 06:46 | Morgawr | and if @r == @x then it works |
| 06:46 | Morgawr | and it does workb |
| 06:47 | Morgawr | but not sure about retries and contention |
| 06:47 | Morgawr | (cause there's none) |
| 06:47 | Morgawr | is there any way to force retrying a transaction? |
| 06:48 | AeroNotix | Make two transactions |
| 06:48 | AeroNotix | (do (future (dosync ...)) (future (dosync))) |
| 06:54 | Morgawr | (doseq [x (range 1 100)] (future (dosync (Thread/sleep (rand-int 100)) (alter r inc) (let [b (agent 0)] (send b (fn [_] (swap! x inc)))) (alter f dec))) (future (dosync (alter inc f) (alter r dec)))) |
| 06:54 | Morgawr | this doesn't seem to be working |
| 06:55 | Morgawr | x is 0 :| |
| 06:55 | Morgawr | (f and r are refs, x is an atom) |
| 06:56 | AeroNotix | what do you mean "not working" ? |
| 06:56 | Morgawr | also it doesn't make sense, f and r should both be 0 but they become -99 and 99 |
| 06:56 | Morgawr | AeroNotix: I mean I'm getting weird results |
| 06:56 | AeroNotix | Morgawr: explain |
| 06:57 | dissipate | what happens if you use memoize on a non-referentially transparent function? |
| 06:57 | Morgawr | only one of the two transactions seems to be running, not the other, because f and r should be = 0 at the end (each transaction cancels out the other) |
| 06:58 | Morgawr | and on top of that the atom is never modified |
| 07:00 | AeroNotix | dissipate: sadness happens |
| 07:00 | AeroNotix | Morgawr: show a full example |
| 07:00 | amalloy | Morgawr: that last code snippet you pasted is completely bonkers. you're trying to swap! x, which is an integer and not an atom; you're trying to (alter inc f) instead of (alter f inc) |
| 07:01 | Morgawr | amalloy: oh.. derp I used x as name for the doseq counter.... that's why |
| 07:01 | Morgawr | and I fucked up the syntax |
| 07:01 | Morgawr | welp |
| 07:03 | Morgawr | okay now x is modified accordingly, I still don't understand why 99 transactions using dec on f and inc on r and 99 transactions using inc on f and dec on r aren't giving me 0 and 0 as values for f and r |
| 07:03 | Morgawr | unless I messed up something else too |
| 07:03 | dissipate | AeroNotix, no, really what happens? it seems that for any given unique set of inputs to the function only 1 side effect is allowed, since subsequent calls will just yield the previous result. what kind of function is that? |
| 07:03 | Kneiva | hmm, changing back to 1.5.1 does not remove the problem |
| 07:06 | AeroNotix | dissipate: try it ! |
| 07:06 | dissipate | amalloy, is it common for programmers to abuse atoms? :( |
| 07:06 | AeroNotix | Do people forget they have a REPL |
| 07:06 | Morgawr | okay, no idea what fixed it but I copypasted my code into an editor, formatted it properly and eval'd it and it works now, thanks AeroNotix and amalloy, sorry for the stupid question |
| 07:08 | dissipate | AeroNotix, i know what will happen, i'm just wondering what type of function it is. it's not referentially transparent since it can still have side effects, but it's side effects are only triggered once per unique set of arguments. |
| 07:10 | Morgawr | dissipate: as far as I can see in the implementation of memoize (source memoize), it stores the result of each function call with a specific argument set inside a map |
| 07:10 | Morgawr | and if on a subsequient function call that key/val pair is found then it returns the result alread in the map |
| 07:10 | Morgawr | else it runs the function |
| 07:10 | Morgawr | so it makes sense that the function is only run once (with its side effects or whatever) |
| 07:11 | dissipate | Morgawr, right. so the function becomes 1 to 1 (one output for a given input), but not referentially transparent since the output can vary across initial calls to the function. is there a computer science term for this kind of function? |
| 07:12 | Morgawr | dissipate: https://en.wikipedia.org/wiki/Memoization this? |
| 07:14 | dissipate | Morgawr, "A function can only be memoized if it is referentially transparent; that is, only if calling the function has exactly the same effect as replacing that function call with its return value." |
| 07:15 | dissipate | Morgawr, if that's true then Clojure's 'memoize' is implemented incorrectly. it should throw an exception if you try to memoize a non-referentially transparent function. |
| 07:15 | Morgawr | how can you check if a function is non-referentially transparent? it means it ought to have no side effects |
| 07:15 | Morgawr | and then we enter into haskell's domain |
| 07:16 | Morgawr | what clojure does to implement memoize is run the function once and rememer its output, it's up to you as a developer to make sure you're not memoizing non-pure functions |
| 07:17 | AeroNotix | dissipate: it's still a non-referentially transparent function, just because you cache the results doesn't give it a separate term |
| 07:18 | dissipate | Morgawr, hmm, that's an interesting question. is there a test you can apply to detect if a function is not pure? |
| 07:19 | dissipate | AeroNotix, sounds correct. and so it is probably considered bad practice to memoize a non-pure function? |
| 07:19 | Morgawr | dissipate: I'm not an expert on the matter but it's definitely an interesting question. It needs to be a static check, obviously, so you need a proper type system that is able to store details on purity, which again leads me to think about IO monad in haskell |
| 07:22 | dissipate | Morgawr, yep. sounds like a good problem domain for Haskell. in Clojure it would probably be extremely difficult if not impossible to determine the purity of a function programmatically |
| 07:25 | AeroNotix | dissipate: you can |
| 07:25 | AeroNotix | wrap code in (io!) |
| 07:25 | AeroNotix | this is used in dosync blocks |
| 07:26 | AeroNotix | Clojure 1.6.0 |
| 07:26 | AeroNotix | user=> (defn foo [] (io! (println "foo"))) |
| 07:26 | AeroNotix | #'user/foo |
| 07:26 | AeroNotix | user=> (dosync (foo)) |
| 07:26 | AeroNotix | IllegalStateException I/O in transaction user/foo (NO_SOURCE_FILE:1) |
| 07:26 | AeroNotix | dissipate: Perhaps memoize could use similar semantics |
| 07:27 | Morgawr | this is only for I/O though, not for all types of impurity |
| 07:27 | AeroNotix | Morgawr: what other types are there? |
| 07:27 | Morgawr | anything that has side effects |
| 07:27 | Morgawr | like swapping on an atom for example |
| 07:27 | Kneiva | martinklepsch: ah, my mistake. I had test namespace that I forgot to suffix with -test, which caused a dependency loop. strange that it didn't show up earlier though. I think I ran the server without problems after creating that file, but I'm not 100 % sure. |
| 07:27 | AeroNotix | Morgawr: sure |
| 07:28 | dissipate | AeroNotix, but the programmer has to use io! |
| 07:28 | AeroNotix | dissipate: sure |
| 07:28 | martinklepsch | Kneiva, that was indeed hard to see :D |
| 07:29 | Morgawr | dissipate: I'd say there can be two ways to detect unsafe operations at runtime, in general, the first one would be to analyze the code (bytecode, JIT, whatever) before executing functions and see if there are any known operations that lead to side effects |
| 07:29 | AeroNotix | sigh |
| 07:29 | Morgawr | and the other would be to run the code and detect if it tries to access some "unsafe" memory areas |
| 07:29 | Morgawr | tainted areas or whatever |
| 07:29 | AeroNotix | double sigh |
| 07:29 | Morgawr | but in any way, that's outside the scope of clojure and it's a big research problem |
| 07:31 | Morgawr | 4 |
| 07:32 | Morgawr | whops, sorry |
| 07:32 | dissipate | Morgawr, i thought the problem had been solved. in Haskell everything is pure, with side effects relegated to monads, no? |
| 07:32 | Morgawr | dissipate: that's at compile time though |
| 07:32 | Morgawr | not runtime |
| 07:34 | dissipate | Morgawr, so you are saying it's possible to have side effects outside of monads in Haskell at runtime? |
| 07:34 | AeroNotix | lol |
| 07:35 | Morgawr | your whole pc is a box full of side effects :P |
| 07:35 | Morgawr | but that's not what I said, I'm just saying that haskell is statically type checking your code to make sure it does what it's supposed to do |
| 07:35 | Morgawr | (although haskell does have unchecked exceptions -I think- and unsafe blocks) |
| 07:41 | dissipate | Morgawr, as i suspected, memoize is using swap!. that being the case, i'm wondering why it's not memoize! instead |
| 07:42 | Morgawr | for the same reason why alter is not alter! or send is not send! |
| 07:42 | Morgawr | also memoize is using an atom internally, the developer doesn't really need to know |
| 07:43 | dissipate | Morgawr, not sure what that means, but ok. i thought the rule was that a function that uses a side effect function like swap! should itself end in ! |
| 07:44 | TEttinger | I think the ! is more for destructive in-place modification |
| 07:44 | TEttinger | probably wrong |
| 07:44 | Morgawr | I don't really know why ! is in some functions and not in others and I don't really like this "inconsistency" in style but I have to deal with it :P |
| 07:45 | Morgawr | I'd use ! in every reftype operation just to have consistency but apparently this is not the case, so idk |
| 07:45 | dissipate | Morgawr, so your argument is that because the swap! is in a closure and isn't relevant to the programmer, the function name is memoize and not memoize! |
| 07:46 | Morgawr | no, my argument is that it's memoize because somebody decided so, I don't know about the actual reasoning |
| 07:47 | Morgawr | and yes, since the atom is just internal implementation it doesn't really matter because it's never exposed |
| 07:47 | TEttinger | ah! |
| 07:47 | TEttinger | Use the bang! only for things not safe in an STM transaction. |
| 07:47 | TEttinger | http://dev.clojure.org/display/community/Library+Coding+Standards |
| 07:48 | Morgawr | TEttinger: makes sense |
| 07:48 | dissipate | TEttinger, a 'destructive' in-place modification occurs within memoize. but the atom is part of the closure returned by memoize, so the programmer isn't really affected by it. |
| 07:48 | dissipate | TEttinger, but that raises the question. is it safe to memoize a function within a transaction? |
| 07:49 | dissipate | or rather to call a memoized function within a transaction |
| 07:49 | Morgawr | dissipate: that's a good question, considering the behavior (the memoized function should be referentially transparent), I'd say yes |
| 07:49 | TEttinger | all I gotta say is question mark |
| 07:49 | Morgawr | even in the worst case, a transaction is retried and the function is called again |
| 07:49 | Morgawr | but instead it already has the cached value |
| 07:49 | TEttinger | http://www.badlogicgames.com/wordpress/?p=3428 time to make a lein plugin for this? |
| 07:50 | Morgawr | for play-clj? |
| 07:51 | TEttinger | no, for in general |
| 07:51 | dissipate | Morgawr, hmm. and the transaction isn't going to detect the swap! occurring in the memoized function and then throw an exception? |
| 07:51 | Morgawr | TEttinger: ah, I only read the title...but I see now, cool stuff |
| 07:52 | Morgawr | dissipate: no, you can use swap! and other side-effecting functions inside a transaction, you shouldn't but you can |
| 07:52 | Morgawr | if you do, just make sure they are idempotent and don't create problem when retried, which is the case of memoized functions |
| 07:52 | Morgawr | s/of/for/ |
| 07:53 | dissipate | hmm, i'm not sure if that's good. it means the judgement is left to the programmer |
| 07:54 | Morgawr | it's definitely not a good thing to do |
| 07:54 | Morgawr | the only way you can prevent running I/O stuff and side-effecting functions inside a transaction is to make sure the programmer uses io! in every unsafe part of the code |
| 07:55 | Morgawr | what I do in my game engine is a (very ugly) trick, I spawn a temporary agent and send some code with side effects, that code will be sent and run only when the transaction commits |
| 07:56 | Morgawr | I do it to query requests to the audio engine from any thread without fear of replaying the request |
| 07:56 | Morgawr | (like play/pause/stop audio) |
| 07:56 | Morgawr | https://github.com/Morgawr/Cloister-Engine/blob/master/src/cloister/sound.clj#L191 <- this |
| 07:56 | Morgawr | it's not the best but it seems to work |
| 07:56 | AeroNotix | I'm sure I've seen something that is guaranteed to do things once |
| 07:57 | AeroNotix | I'm afk at the moment |
| 07:57 | AeroNotix | ,(once-only) |
| 07:57 | clojurebot | #<CompilerException java.lang.RuntimeException: Unable to resolve symbol: once-only in this context, compiling:(NO_SOURCE_PATH:0:0)> |
| 07:57 | Morgawr | that's for macros |
| 07:57 | AeroNotix | ,once-only |
| 07:57 | clojurebot | #<CompilerException java.lang.RuntimeException: Unable to resolve symbol: once-only in this context, compiling:(NO_SOURCE_PATH:0:0)> |
| 07:57 | AeroNotix | ah yes |
| 07:58 | dissipate | Morgawr, i see, interesting |
| 08:02 | dissipate | Morgawr, by using the agent in the transaction there is no possibility for a retry? |
| 08:02 | amatsu | Is there an official #clojure pastebin? |
| 08:02 | Morgawr | amatsu: refheap.com but not sure how "official" it is |
| 08:03 | Morgawr | dissipate: all the send operations sent to agents inside a transactions are stored in the thread-local queue for the transaction and only sent once when the transaction is committed |
| 08:03 | Morgawr | so in case of retries they aren't sent |
| 08:04 | amatsu | OK. So I was wondering whether this is particularly idiomatic Clojure https://www.refheap.com/85088 |
| 08:04 | dissipate | Morgawr, and why is that considered to be a 'hack'? sounds good to me |
| 08:05 | Morgawr | dissipate: because it's spawning an extra thread (well, agent) just to perform that operation and it looks very ugly to me. But yeah, it works for me at the moment and in the future I'll probably go back to it and see if there's more elegant way of doing it |
| 08:06 | TEttinger | that looks perfectly reasonable, amatsu |
| 08:07 | amatsu | TEttinger: alright, thanks! |
| 08:07 | dissipate | Morgawr, are you trying to make Cloister-Engine *the* game engine for clojure? |
| 08:07 | TEttinger | I should ask, amatsu, did the transient code work the first time? I have had such bad luck with transients! |
| 08:07 | Morgawr | nah, I'm trying to make cloister-engine a usable game engine |
| 08:08 | Morgawr | play-clj is probably a better option |
| 08:08 | dissipate | Morgawr, what kinds of games is it going to support? |
| 08:08 | Morgawr | my project is just an experiment to see how it goes with a lot of threads and semi-uncoordinated entities in a game |
| 08:08 | Morgawr | 2D games |
| 08:08 | Morgawr | it's still far from being done |
| 08:08 | Morgawr | currently working on the physics engine which is tricky because it's a non-threadsafe java engine and I'm wrapping around it |
| 08:09 | dissipate | wow, the physics namespace is pretty short |
| 08:09 | amatsu | TEttinger: in my first attempt I didn't realise that you had to use the return value of persistent! (I thought it would mutate the value) |
| 08:09 | amatsu | TEttinger: once I figured that out it worked perfectly. |
| 08:09 | Morgawr | dissipate: well, it's incomplete :P |
| 08:10 | Morgawr | I mean, it runs physics thread but there's still no function to set physical bodies yet lol |
| 08:10 | dissipate | Morgawr, i see. i'm doing a game thing myself. but it's games for programmed bots, not human players. |
| 08:10 | Morgawr | dissipate: ah, cool |
| 08:13 | TEttinger | dissipate, what's that used for, AI research? |
| 08:14 | dissipate | Morgawr, yeah, my reference game is called 'Matching Pennies', which is a game that results in approximations to a really hard computer science problem, but the game server could be set up for a lot of other games (chess, checkers etc.). unfortunately, coding is going slow since i'm learning clojure and some other technologies all at once. :O |
| 08:14 | Morgawr | dissipate: good luck then :) Clojure is a great language |
| 08:14 | dissipate | TEttinger, yep, pretty much. but with a twist. |
| 08:17 | dissipate | TEttinger, the game server will allow contestants to submit Docker images, so they can implement their bots in any language/framework. then, on top of that i plan on adding wagering using Bitcoin. so the bots can be played against each other 24/7 for money. |
| 08:21 | dissipate | TEttinger, more info here https://github.com/dissipate/matching-pennies-test |
| 09:49 | akazlou | hi, I found that map (data structure) behaves good with vector of 2 elements, for example, (into {} [[1 2] [3 4] [5 6]]) produces map with the corresponding key/value, which is nice, the question is where it is mentioned such kind of interchangeability between vectors and maps |
| 09:50 | TEttinger | I think that's a special property of into, but I'm not sure |
| 09:51 | akazlou | also (conj {} [1 2]), but this one is mentioned here http://clojure.org/data_structures on the Maps section |
| 09:51 | TEttinger | err yes I messed up |
| 09:51 | TEttinger | into uses conj internally |
| 09:52 | akazlou | o, nice |
| 09:52 | TEttinger | into is equivalent to (fn [to from] (reduce conj to from)) |
| 09:52 | TEttinger | for non-editable collections, anyway |
| 09:52 | TEttinger | for editable ones it uses transients |
| 09:56 | akazlou | all right, read the doc of into once again, it is mentioned that the items are conjoined, so that is why, question is solved :) |
| 09:56 | akazlou | thanks TEttinger |
| 09:57 | TEttinger | heh no prob |
| 09:57 | TEttinger | it's usually more active here... maybe the san francisco people aren't up yet :-) |
| 09:58 | akazlou | :) np, as far as there are people who can help |
| 10:20 | caulagi | I want to do - (-> (add "joe" 2) (add "mary" 3)) |
| 10:20 | caulagi | Why is this wrong? (defn add [student grade] {}) |
| 10:20 | caulagi | ArityException Wrong number of args (3) passed to: user$add clojure.lang.AFn.throwArity |
| 10:21 | ptcek | -> macro adds the result of previous form as the second element in the next form |
| 10:22 | ptcek | thus the second form after -> expands to (add {} "marry" 3) |
| 10:23 | ptcek | caulagi: what do you want to achieve? |
| 10:27 | caulagi | ptcek: I am trying the problems at exercism.io. Can you please look at http://pastie.org/9139521? |
| 10:28 | caulagi | the add function still gives the arity exception |
| 10:34 | TEttinger | caulagi, it looks like exercism.io is not public. can you tell us what you are trying to do on this task? |
| 10:35 | martinklepsch | are there any alternatives to enlive for scraping that one should be aware of? |
| 10:36 | ptcek | martinklepsch: laser ? |
| 10:37 | caulagi | ptcek, TEttinger: https://github.com/caulagi/exercism/tree/master/clojure/grade-school |
| 10:37 | caulagi | I want to make the test pass for adding students here - https://github.com/caulagi/exercism/blob/master/clojure/grade-school/grade_school_test.clj |
| 10:39 | caulagi | I am confused with the use -> for the tests. Shouldn't my function definition here work? http://pastie.org/9139521 |
| 10:40 | TEttinger | so db is a hashmap, let's say. {} is how it is defined |
| 10:41 | ptcek | (defn add [db student grade] ...) must work |
| 10:41 | TEttinger | when you use -> , it takes the first arg to -> , which starts as {} there, and makes it the first arg passed to add |
| 10:42 | TEttinger | so since add is always given two args, plus the one given it by ->, the fn should be defined as taking 3 args: db (a map), student (a string) and grade (a number) |
| 10:43 | TEttinger | and it should return a map |
| 10:44 | caulagi | TEttinger, ptcek: got it. I have to use db as the first arg for all my functions. Thanks! |
| 10:44 | TEttinger | no prob |
| 10:45 | ptcek | caulagi: fyi, ->> is works the same but inserts the result of previous form as the last arg of the next form, quite useful too... |
| 10:46 | caulagi | ptcek: is ->> more idiomatic? |
| 10:47 | ptcek | no, the same |
| 10:49 | caulagi | So -> is the way to chain functions together, right? With the output from the previous function serving as the first input for the next? |
| 10:51 | ptcek | right, and ->> inserts the previous result as last argument |
| 10:53 | Morgawr | https://github.com/rplevy/swiss-arrows there's also this |
| 10:53 | Morgawr | I love the diamond wand |
| 10:53 | Morgawr | it should be part of clojure.core imo :P |
| 11:03 | Glenjamin | "-<><:p" |
| 11:03 | Glenjamin | that seems like a joke. |
| 11:11 | `szx | Morgawr: isn't the diamond wand the same as as-> ? |
| 11:13 | Glenjamin | i think so, with <> instead of explicit naming |
| 11:22 | bbloom | Morgawr: TEttinger: heh, neat, didn't realize manuel simoni put eclj on his list of kernel-likes -- it's probably a bit premature, since it doesn't really expose a vau-like primitive yet |
| 11:22 | bbloom | although the interpreter does have first class environments |
| 11:56 | martinklepsch | is there no such thing as select1 in enlive? |
| 12:35 | sritchie | hey guys - is there a mapcat equivalent for core async? meaning, take a function of channel and item -> channel that returns a new channel? |
| 12:36 | bbloom | sritchie: https://github.com/clojure/core.async/blob/master/src/main/clojure/clojure/core/async.clj#L524-L552 |
| 12:37 | sritchie | bbloom: that takes a function of item => collection |
| 12:37 | sritchie | I want something that takes a function of item => new channel of item |
| 12:37 | sritchie | monadic bind |
| 12:38 | bbloom | sritchie: those mapcat functions do not produce or consume collections, they operate on channels |
| 12:38 | sritchie | " f must return a |
| 12:38 | bbloom | sritchie: it's the f function that returns collections |
| 12:38 | sritchie | collection." |
| 12:38 | sritchie | exactly - my f returns a channel |
| 12:39 | sritchie | I have a function that takes a stripe event id and returns a channel of a stripe event |
| 12:39 | sritchie | and I have a channel of stripe IDs that come in from a webhook |
| 12:39 | sritchie | so I want to take that channel and that function and return a new channel that produces stripe events |
| 12:40 | bbloom | sritchie: why not parameterize your code with a channel to report back to? |
| 12:40 | sritchie | sure, I can do that, or I can use "pipe" in core.async, looks like |
| 12:40 | sritchie | but what I'm describing is the monadic bind, |
| 12:41 | sritchie | which has some pretty big advantages with the operators you can build on top of it |
| 12:41 | bbloom | sritchie: i'm quite familar with monadic bind, but you haven't specified for which monad :-P |
| 12:41 | sritchie | the channel monad |
| 12:41 | bbloom | not a thing. |
| 12:42 | sritchie | why not? It's like the Future monad |
| 12:42 | sritchie | I can write a bind and a return |
| 12:42 | bbloom | you're talking about a stream monad of sorts on an individual channel |
| 12:42 | sritchie | that's correct |
| 12:42 | bbloom | but close & multiplexing and other aspects of CSP means that monad doesn't really exist, as far as i can tell |
| 12:43 | bbloom | you can overlay one on a subset of the functionality of channels |
| 12:43 | sritchie | which would still be helpful in this case |
| 12:43 | bbloom | it might be, but i suspect that you're overlooking a simpler solution :-) |
| 12:45 | sritchie | bbloom: how about https://gist.github.com/sritchie/fddf0c13708f651ef8c3 |
| 12:46 | bbloom | sritchie: 1) you need to return the out channel |
| 12:46 | martinklepsch | is there no such thing as select1 in enlive? |
| 12:46 | sritchie | oh, whoops |
| 12:46 | bbloom | sritchie: 2) you're not handling what hapepns if chan is closed |
| 12:46 | bbloom | 3) should call that argument "in" as is the convention now |
| 12:47 | bbloom | 4) that's map, not flatmap, you need an inner loop to accomplish a flatmap |
| 12:48 | bbloom | sritchie: to do what you want to do, you can just map pipe |
| 12:54 | roelof | hello, why do I see a dependency cycle on this script : http://pastebin.com/5Pk0XpvX |
| 12:55 | bbloom | roelof: def is for toplevels |
| 12:55 | bbloom | roelof: use let |
| 12:55 | bbloom | roelof: actually, you have quite a few errors in there |
| 12:55 | bbloom | roelof: your'e not actually calling == |
| 12:55 | sritchie | bbloom: that actually seems to work |
| 12:55 | sritchie | with your changes |
| 12:55 | sritchie | bbloom: it is in fact flatmap |
| 12:55 | sritchie | bind, rather |
| 12:55 | sritchie | I don't want to worry about sequences inside |
| 12:56 | sritchie | I can handle that with mapcat and into |
| 12:56 | roelof | bbloom : still the same error |
| 12:56 | sritchie | bbloom: https://gist.github.com/sritchie/fddf0c13708f651ef8c3 |
| 12:56 | roelof | and I know there could be more errors . I try to figure out if it's working |
| 12:57 | bbloom | roelof: that code is totally bogus for lots of reasons, but i don't think it should cause a cyclic dependency error... you need to provide more info: exact error, what you're doing to cause it, etc |
| 12:57 | sritchie | oh, wait, I see what you mean |
| 12:57 | sritchie | bbloom: yeah, since we can't just take one thing from each channel, sure |
| 12:57 | bbloom | roelof: it may be best to try something like instarepl in LightTable to tinker with clojure more interactively on smaller expressions |
| 12:58 | Glenjamin | roelof: what guide are you using to learn? |
| 12:58 | roelof | oke, I type it into the editor in intelij idea with the cursive plugin and I send it to the REPL with crtl+enter |
| 12:59 | bbloom | roelof: i suspect that when you try to evaluate an individual form, cursive is trying to load the file & you're getting the error from your ns form |
| 12:59 | bbloom | roelof: try to evaluate something simple like (+ 2 2) |
| 12:59 | roelof | I use two guides : clojure for the brave and i use Iloveponies github and do the 4clojure exercises |
| 12:59 | bbloom | sritchie: i did lots of experiments with the "stream monad" if you want to call it that, and ultimately decided it's a terrible idea |
| 13:00 | bbloom | sritchie: see https://github.com/brandonbloom/asyncx |
| 13:00 | sritchie | so you think the way is to modify these stripe functions to optionally accept a source channel? |
| 13:00 | sritchie | and sequence that way |
| 13:00 | bbloom | i think that they should EXPLICITLY, not optionally, take a channel on which to report their results |
| 13:02 | sritchie | any killer example of why this flatmap pattern is so bad? |
| 13:02 | sritchie | for this subset of core.async functionality? |
| 13:04 | bbloom | it's not flatmap that's bad |
| 13:04 | roelof | bbloom : I think it s a cursive problem. Even with a new project ( + 2 2 ) gives the same error |
| 13:04 | bbloom | roelof: i strongly recommend light table for learning |
| 13:04 | bbloom | roelof: instarepl is your friend |
| 13:04 | bbloom | roelof: it "just works" |
| 13:05 | bbloom | sritchie: the issue is that CSP exists to coordinate independent processes & just happens to not be totally useless for stream processing |
| 13:05 | bbloom | sritchie: that doesn't mean you should force everything in to a stream processing problem |
| 13:05 | roelof | oke, I asked some help in the forum how I can make a something that sits on a github and how I send something to REPL but no answers there |
| 13:05 | roelof | that why I do not use light table |
| 13:05 | bbloom | sritchie: if you genuinely have a very linear stream, there are much better approaches |
| 13:06 | sritchie | the stream here is - accept a stripe webhook, hit stripe with the provided ID to verify that everything is good to go, filter errors, then provide a channel that kicks out valid webhook events |
| 13:06 | bbloom | roelof: i have no idea what "sits on github" means and sending stuff to the repl is entirely automatic in instarepl |
| 13:06 | sritchie | bbloom: so in this case I'm using core.async to handle that streaming problem, then to kick off a number of different events (send email, mark payment transfers as complete, etc) |
| 13:07 | roelof | bbloom : I do these lessons : http://iloveponies.github.io/120-hour-epic-sax-marathon/structured-data.html |
| 13:08 | TravisD | lol, that url. |
| 13:08 | roelof | how I can make them work on light table and how I do lein midje so I can see if I do it right |
| 13:08 | bbloom | sritchie: argh. ok, so i'm not sure whether i should help you with your current problem or talk you out of webhooks lol |
| 13:09 | sritchie | haha, nice |
| 13:09 | sritchie | bbloom: sure, talk me out of them |
| 13:09 | bbloom | sritchie: what happens if your server is down? you're going to need to implement batch synching anyway |
| 13:09 | bbloom | start with that |
| 13:09 | bbloom | don't start with webhooks |
| 13:09 | bbloom | b/c you dont strictly need that |
| 13:09 | bbloom | just start with a cron job that fetches everything since the last cron job & does a batch process |
| 13:10 | roelof | TravisD: why lol. is it a bad way to learn clojure ? |
| 13:10 | sritchie | bbloom: yeah, fair |
| 13:10 | bbloom | sritchie: after that, only use webhooks to accomplish realtime goals |
| 13:10 | bbloom | sritchie: but chances are cron every minute will be good enough |
| 13:11 | TravisD | roelof: not sure, I didn't look closely. But I chuckled because the url contains "120-hour-epic-sax-marathon", yet it's a legitimate clojure tutorial |
| 13:12 | sritchie | bbloom: I was going to go this road for mailing list unsubscribes, |
| 13:12 | sritchie | since mandrill doesn't expose an API to check on unsubscribes that have occurred recently |
| 13:13 | sritchie | bbloom: I hear you on doing batch processing first |
| 13:15 | sritchie | bbloom: not as convinced that having these stream processing operators isn't helpful for core.async, but sure, I may be reaching for stream processing because I'm familiar with it |
| 13:16 | bbloom | sritchie: http://help.mandrill.com/entries/22285907-What-happens-if-my-webhook-URL-is-down-or-can-t-accept-requests- *cringe* 100 retries lol |
| 13:16 | cbp | How do I delete projects on clojars |
| 13:16 | bbloom | sritchie: presumably you can get the list of all subscribed people right? i'd do that and take set difference |
| 13:18 | bbloom | sritchie: again, stream processing operators isn't the problem, it's the "stream monad" you're imagining... it simply is a bad fit. you don't want to use function composition to create pipelines. CSP isn't composed via functions, it's composed via channel sharing |
| 13:19 | bbloom | sritchie: monads are a way to create *linear* execution out of dataflow. but core async is about non-linear and non-deterministic dataflow, things you can only simulate with a monad by wrapping it in an interpreter of sorts at the top level, but you can't intercept/modify the semantics of core.async's top level |
| 13:19 | bbloom | like runState or whatever in haskell, you simply don't have access to that thing in core.async |
| 13:19 | sritchie | okay, sure |
| 13:20 | bbloom | but yeah, every time i've ever used webhooks, i greatly regretted it :-P |
| 13:23 | sritchie | bbloom: ugh, yeah. If only the real world didn't intrude :) |
| 13:27 | mi6x3m | hey clojure, aside from the creation step, what is the difference between def and alter-var-root |
| 13:29 | bbloom | mi6x3m: alter-var-root takes a function of the current var root |
| 13:29 | mi6x3m | bbloom: yeah i'm clear on this, but is this all? |
| 13:29 | bbloom | mi6x3m: def is a special form and has non-standard evaluation. alter-var-root is just a normal function |
| 13:30 | mi6x3m | OK, I see, but the net effect is more or less the same? |
| 13:31 | mi6x3m | changing the root binding |
| 13:31 | roelof | Can anyone help me with a workflow that works with that url ??? |
| 13:31 | lazybot | roelof: Yes, 100% for sure. |
| 13:32 | roelof | What is then a good workflow that works with that url and light table ?? |
| 13:32 | lazybot | roelof: Definitely not. |
| 13:32 | sritchie | bbloom: so cron it is, I guess. back into writing deploy code |
| 13:32 | bbloom | mi6x3m: the second call to (def foo x) is equiv to the first call of (alter-var-root #'foo (constantly x)) |
| 13:32 | bbloom | (inc cron) ; just for you sritchie! |
| 13:32 | lazybot | ⇒ 1 |
| 13:33 | bbloom | mi6x3m: what are you trying to accomplish? |
| 13:33 | sritchie | :) |
| 13:33 | mi6x3m | bbloom: absolutely nothing, just getting a clear picture on this :) |
| 13:34 | bbloom | mi6x3m: see the intern and alter functions: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Var.java |
| 13:34 | bbloom | mi6x3m: def is the sepcial form version of ##(doc intern) |
| 13:34 | lazybot | java.lang.SecurityException: You tripped the alarm! intern is bad! |
| 13:34 | bbloom | (doc intern) ; stupid lazybot |
| 13:34 | clojurebot | "([ns name] [ns name val]); Finds or creates a var named by the symbol name in the namespace ns (which can be a symbol or a namespace), setting its root binding to val if supplied. The namespace must exist. The var will adopt any metadata from the name symbol. Returns the var." |
| 13:35 | roelof | bbloom : can you help me with a good workflow on the github page and light table ? |
| 13:36 | bbloom | roelof: i'm not sure what you need... if you're really having that hard a time, install leinigen, type `lein repl` and then use copy paste from any editor to the repl in the terminal.. do that until you understand what's going on |
| 13:37 | roelof | bbloom : I try to figure out how I can make that github page avaible in Light table ? |
| 13:37 | bbloom | roelof: copy/past |
| 13:37 | bbloom | i have no idea what "make available" means |
| 13:37 | bbloom | use copy paste for code fragments |
| 13:38 | bbloom | sheesh |
| 13:38 | roelof | thanks. I will try that |
| 13:46 | seangrov` | bbloom: I hadn't realized that Haiku has been using ALM, pretty cool (along with their editor for it, ALE) |
| 13:47 | bbloom | seangrov`: yeah, spotted that at somepoint, but i don't know jack about haiku |
| 13:47 | bbloom | other than it's some approximation of a smalltalk environment written in c++ |
| 13:47 | bbloom | ... kinda like Microsoft Office ;-) |
| 13:48 | seangrov` | I think it's a rewrite of BeOS with document-oriented GUIs (not sure what that means though) |
| 13:49 | seangrov` | Ah, I see. It means that GUI's are serializable like documents, and editable by end-users |
| 13:50 | bbloom | seangrov`: yeah, it's got an ABI similar to Window's COM |
| 13:50 | bbloom | hence the office joke |
| 13:51 | seangrov` | Yeah, I don't think I would have got that joke ;) |
| 13:53 | bbloom | yeah, ppl don't realize that modern windows development is very smalltalk like... thanks to COM/OLE |
| 13:53 | bbloom | but smalltalk like with a shitload of C++ cruft that makes you want to cry |
| 13:53 | bbloom | and looks nothing like smalltalk, sadly lol |
| 13:53 | bbloom | but that's my (best available) understanding of beos too |
| 13:53 | seangrov` | Well, for better or for worse... MS knows their developer audience :P |
| 13:53 | bbloom | true story |
| 13:53 | seangrov` | I'm surprised they were able to sell the switch to html5/js internally |
| 13:54 | bbloom | my understanding is that they didn't so much sell it as shove it down an entire orgs throat |
| 13:55 | Glenjamin | is there a builtin (def sum (partial reduce +)) ? |
| 13:56 | seangrov` | ,(reduce + 0 [1 2 3 4 nil 1 2 3 4]) |
| 13:56 | clojurebot | #<NullPointerException java.lang.NullPointerException> |
| 13:56 | Glenjamin | clearly the definition is trivial, but i feel like most stdlibs with a reduce also include sum |
| 13:56 | seangrov` | ,(reduce (fnil + 0) [1 2 3 4 nil 1 2 3 4]) |
| 13:56 | clojurebot | #<NullPointerException java.lang.NullPointerException> |
| 13:57 | seangrov` | ,(reduce (fnil + 0 0) [1 2 3 4 nil 1 2 3 4]) |
| 13:57 | clojurebot | 20 |
| 13:57 | seangrov` | There we go... |
| 14:20 | TEttinger | Glenjamin, core.matrix has it, and a lot of other non-scalar operations |
| 14:20 | TEttinger | (IIRC) |
| 14:21 | Glenjamin | ok, cheers - just wanted to check i hadn't missed something obvious |
| 14:21 | TEttinger | it's easy enough with reduce + or apply + |
| 14:25 | Glenjamin | yeah, i've just def-ed sum to improve readability in a big function a bit |
| 14:36 | jdeisenberg | Hi. I'm getting the impression from my reading that vectors are preferable to lists for most cases. |
| 14:36 | jdeisenberg | is this accurate? |
| 14:37 | bbloom | jdeisenberg: yes |
| 14:37 | bbloom | jdeisenberg: it's worth understanding them though |
| 14:37 | bbloom | i mean the differences between the two |
| 14:37 | bbloom | ~colls |
| 14:37 | clojurebot | colls is seqs and colls |
| 14:37 | bbloom | ~seqs |
| 14:37 | clojurebot | seqs is http://www.brainonfire.net/files/seqs-and-colls/main.html |
| 14:37 | bbloom | jdeisenberg: ^^ |
| 14:38 | jdeisenberg | Thanks; I'll read it. |
| 14:40 | jdeisenberg | Hm. sequential? and seq? are two entirely different things. |
| 14:41 | bbloom | jdeisenberg: "does this object represent a sequential progression of values?" vs "does this object provide first/next lazy sequence operations?" |
| 14:42 | bbloom | jdeisenberg: the former (and then some) can be converted to the later by way of the "seq" function |
| 14:42 | jdeisenberg | Yes. I can imagine that as a source of confusion to beginners. |
| 14:42 | bbloom | it is, hence that page :-) |
| 14:42 | jdeisenberg | Except the page doesn't state the difference as succinctly and directly as you did :) |
| 14:43 | bbloom | jdeisenberg: i believe https://github.com/timmc/seqs-and-colls accepts pull requests, have at it :-P |
| 14:55 | TimMc | confirmed |
| 14:58 | tuft | where do i get clojure.contrib.repl-utils from? |
| 14:59 | jdeisenberg | OK thanks. |
| 15:00 | PinkPrincess | Hello, people. Can anyone help me with what I suspect is a fairly simple macro problem? |
| 15:00 | bbloom | ~anyone |
| 15:00 | clojurebot | Just a heads up, you're more likely to get some help if you ask the question you really want the answer to, instead of "does anyone ..." |
| 15:00 | PinkPrincess | Right. |
| 15:01 | PinkPrincess | https://www.refheap.com/85097 almost works, but I need line 7 to refer to the network# defined on line 3. |
| 15:01 | PinkPrincess | How do I achieve that? |
| 15:01 | bbloom | just drop the ` |
| 15:01 | bbloom | the inner one that is |
| 15:01 | bbloom | oh wait |
| 15:01 | bbloom | nevermind, you have that inside a ~ |
| 15:01 | bbloom | you need to use an explicit gensym |
| 15:01 | PinkPrincess | Ah. |
| 15:02 | bbloom | ,(let [sym (gensym)] `~sym) |
| 15:02 | clojurebot | G__27 |
| 15:03 | bbloom | PinkPrincess: but you can do it w/o that here |
| 15:03 | bbloom | assuming command is some kind of seq, you can just do something like: |
| 15:03 | PinkPrincess | Without the gensym? |
| 15:04 | PinkPrincess | command is going to be (f & args) for some commands and any number of args. |
| 15:04 | bbloom | ,(let [cmd '(f x)] `(~@cmd x#)) |
| 15:04 | clojurebot | (f x x__50__auto__) |
| 15:04 | PinkPrincess | And I need to inject network as the first arg. |
| 15:04 | bbloom | PinkPrincess: use ~@ inside a list to reconstruct the form you want |
| 15:05 | bbloom | ,(let [cmd '(f a b)] `(~(first cmd) x# ~@(next a))) |
| 15:05 | clojurebot | #<CompilerException java.lang.RuntimeException: Unable to resolve symbol: a in this context, compiling:(NO_SOURCE_PATH:0:0)> |
| 15:05 | bbloom | ,(let [cmd '(f a b)] `(~(first cmd) x# ~@(next cmd))) |
| 15:05 | clojurebot | (f x__99__auto__ a b) |
| 15:05 | bbloom | got it? |
| 15:05 | PinkPrincess | Think so. I'll just see if I can make sense of it in my own code. |
| 15:06 | tuft | what's the best way to introspect a java object in the repl? |
| 15:06 | bbloom | PinkPrincess: the # suffix works within the context of a single ` syntax-quote. you can use an explicit gensym to share a generated symbol between syntax-quoted forms, but in this case, you simply want to use two different unquotes for either side of the command you want to splice, rather than try to do it in a single ~ |
| 15:07 | justin_smith | tuft I think that now you can just use clojure.repl |
| 15:07 | PinkPrincess | Yeah, makes sense. |
| 15:07 | justin_smith | tuft and that comes with clojure.core |
| 15:07 | bbloom | justin_smith: i think he means like pretty print or look at private fields or whatever |
| 15:08 | justin_smith | PinkPrincess: please share a paste of the code |
| 15:08 | bbloom | tuft: when i really need to look inside java objects a bunch, that's the only time i ever boot up intellij/cursive & use their inspector :-) |
| 15:08 | tuft | bbloom: yeah, that's basically what i'm doing =) |
| 15:08 | tuft | justin_smith: hmm, i'm not seeing the "show" function in that namespace |
| 15:08 | justin_smith | yeah, that ` is out of place |
| 15:09 | tuft | clojure.reflect seems to have some stuff though |
| 15:09 | bbloom | tuft: try also ##(doc bean) |
| 15:09 | lazybot | ⇒ "([x]); Takes a Java object and returns a read-only implementation of the map abstraction based upon its JavaBean properties." |
| 15:09 | PinkPrincess | Just updated it, justin_smith: https://www.refheap.com/85097 |
| 15:09 | bbloom | ,(bean {}) |
| 15:09 | clojurebot | {:empty true, :class clojure.lang.PersistentArrayMap} |
| 15:09 | bbloom | if you're lucky enough to have a class with getFoo and isBar functions all over it |
| 15:09 | PinkPrincess | Seems to work now. Thanks a lot, bbloom. |
| 15:10 | bbloom | PinkPrincess: cool |
| 15:10 | PinkPrincess | At least lein expectations performs as before. Just with 60% less code due to the macro. |
| 15:10 | justin_smith | perhaps a helper function that arranges the code-as data to the form the function should return |
| 15:10 | justin_smith | *code-as-data |
| 15:10 | justin_smith | *the macro should return (no coffee, shutting up now) |
| 15:10 | cbp | anyone knows how to delete a project in clojars |
| 15:11 | tuft | bbloom: ah yeah, bean is handy |
| 15:12 | tuft | bbloom: wish it automatically wrapped return values, though |
| 15:12 | bbloom | tuft: you mean like recursively bean-ify? :-) |
| 15:13 | bbloom | if you look at (source bean) you'll see it's not that tricky, probably could try to write a variant that does that & see what happens :-) |
| 15:13 | tuft | bbloom: yeah exactly -- lazily i suppose |
| 15:13 | bbloom | yeah, definitely needs to be lazy |
| 15:15 | ToxicFrog | Exception in thread "main" clojure.lang.ArityException: Wrong number of args (-2) passed to: PersistentVector, compiling:(spellcast/game.clj:148:26) |
| 15:15 | justin_smith | tuft there is clojure.reflect, but there are also some slightly easier libs that wrap it |
| 15:15 | ToxicFrog | Even with macros, how the hell did I manage to pass a negative number of arguments to something? |
| 15:16 | eg0 | u can have records inside records thats insane |
| 15:16 | bbloom | ToxicFrog: i think that's a side effect of macros getting 2 secret args |
| 15:16 | justin_smith | tuft: also, sometimes I get the info I want from (class foo) + javadoc |
| 15:17 | justin_smith | tuft: or (bean foo) sometimes has useful output |
| 15:17 | bbloom | ToxicFrog: and a bad error message going wrong in some case |
| 15:18 | justin_smith | bbloom: well, it won't be pretty but clojure.reflect will let you see private fields |
| 15:18 | justin_smith | did I meantion anything called "show"? my scrollback is not finding it |
| 15:20 | Glenjamin | hi guys, so i'm playing around with reducers, trying to see if they'll help me speed up a map operation on a large hash-map but i'm getting a cryptic error message ClassCastException: null - https://www.refheap.com/85100 |
| 15:22 | Glenjamin | the folding one works (but is slow), the folding transient just bails out with the ClassCastException |
| 15:26 | justin_smith | if you reify the beans, be sure to make a mexican food based project to put it in |
| 15:26 | justin_smith | lol |
| 15:27 | technomancy | http://p.hagelb.org/eyebrow.gif |
| 15:35 | bbloom | justin_smith: dammit, now i'm hungry |
| 15:37 | bbloom | it really annoys me that protocol vars don't evaluate to an identity value |
| 15:37 | bbloom | ,(defprotocol P) |
| 15:37 | clojurebot | P |
| 15:38 | bbloom | ,(let [x P] (extend-protocol P Object) (= x P)) |
| 15:38 | clojurebot | false |
| 15:38 | bbloom | :-( |
| 15:39 | justin_smith | tuft: oh you mean there is no clojure.repl/show - I just realized what you meant |
| 15:40 | eg0 | in racket you can use default values for functions like (define (hello (user "ego"))(string-append "hello " user) so that (hello) is "hello ego" is there something similar in clojure? |
| 15:41 | Bronsa | eg0: (defn hello ([] (hello "ego")) ([user] (str "hello " user))) |
| 15:42 | eg0 | so then just variable arity |
| 15:53 | justin_smith | I was all lagged, sorry for any lagged out non-sequitors |
| 16:41 | visof | hello, is there anybody have used jiraph? |
| 16:46 | justin_smith | visof: ninjudd is on this channel, though he may be away |
| 16:48 | visof | justin_smith: havn't you used it before? |
| 16:48 | justin_smith | visof: if you have a specific question someone may be able to help, no I have not personally used it |
| 16:49 | justin_smith | I was pointing at ninjudd because he is the author, and he is nominally here |
| 16:49 | visof | justin_smith: i can't use the basic stuff in the tutorial and yesterday i asked here but none help |
| 16:49 | justin_smith | visof: what was the error |
| 16:49 | visof | okay |
| 16:50 | visof | justin_smith: i'll paste what i did project.clj core.clj and error, give me sec |
| 16:53 | visof | justin_smith: error: https://www.refheap.com/85103 , project.clj: https://www.refheap.com/85104 , core.clj: https://www.refheap.com/85105 |
| 16:56 | visof | justin_smith: checked? |
| 17:01 | whodidthis | oh no, figwheel doesnt seem to work with projects in checkouts |
| 17:06 | expez | Any way to get rid of the error message in lighttable? |
| 17:07 | bbloom | expez: you mean like the inline evaluated ones? |
| 17:07 | sveri | hi, I am just wondering if its good style to render a template via enlive, put some edn-data into a hidden input field and take that data into an app-state variabel that can be used by cljs? Or should the app-state always be requested separately? |
| 17:08 | expez | bbloom: found it, had to reach for the mouse to click it away |
| 17:08 | bbloom | expez: i think there is a "clear all" command of some sort |
| 17:08 | bbloom | expez: yeah "Eval: Clear inline results" |
| 17:09 | expez | bbloom: thanks, but I'm really just visiting from Emacs |
| 17:09 | bbloom | expez: it's cool, i'm not visiting at all from vim :-P |
| 17:10 | expez | wanted to go through the Om tutorial and getting clojurescript setup for emacs is a bit underdocumented |
| 17:14 | benmoss | hm, i’m seeing my first put! to a core async chan succeed and the go block reading it successfully read it, but after that the second put! never calls its callback and my go block never sees another value |
| 17:14 | justin_smith | visof: the error says tokyocabinet is missing, and in project.clj you have the dep on tokyocabinet commented out |
| 17:14 | benmoss | i dont really know what would be causing that. the “put!” still returns true |
| 17:16 | justin_smith | visof: also, it may require something in the sonatype snapshot repo https://github.com/ninjudd/jiraph/blob/develop/project.clj#L23 |
| 17:20 | benmoss | ah |
| 17:29 | visof | justin_smith: https://www.refheap.com/851079 |
| 17:30 | visof | justin_smith: i have changed the project.clj |
| 17:34 | justin_smith | visof: that refheap is 404 |
| 17:36 | visof | justin_smith: https://www.refheap.com/85107 |
| 17:41 | justin_smith | that's weird, flatland.jiraph.core definitely exists |
| 17:41 | justin_smith | can you show your updated project.clj? |
| 17:41 | visof | sure |
| 17:42 | visof | justin_smith: https://www.refheap.com/85109 ? |
| 17:44 | justin_smith | visof: that project.clj does not contain the jiraph dep |
| 17:44 | justin_smith | it looks like a copy of the jiraph project.clj, where I was just pointing at the :repositories line indicating where some unfound deps may be |
| 17:44 | visof | so what should be the final ? |
| 17:45 | justin_smith | so instead of copying their whole project.clj, keep yours, and add the :repositories entry they have |
| 17:46 | justin_smith | https://github.com/technomancy/leiningen/blob/master/sample.project.clj the example project.clj has some explanation of what goes in a project.clj, and why the stuff is all there |
| 17:50 | visof | justin_smith: isnt' it dependencies? |
| 17:50 | visof | justin_smith: https://www.refheap.com/85109 there is a dependencies here |
| 17:50 | justin_smith | yes, but you don't actually have a dependency of jiraph |
| 17:50 | justin_smith | so it won't find jiraph |
| 17:50 | justin_smith | which is the error you got |
| 17:51 | justin_smith | that is jiraph's project.clj - it is the dependency list that project uses |
| 17:52 | justin_smith | it isn't the dependency list you should have to use the project (lein figures that stuff out for you) - I just linked to their project.clj to show that some of their deps could be coming from that nonstandard repository, in their :repositories key |
| 17:54 | visof | justin_smith: [org.flatland/jiraph "0.12.3-alpha1"] |
| 17:57 | visof | justin_smith: https://www.refheap.com/85110 that's another error after adding line of jiraph to project.clj |
| 17:58 | justin_smith | ok, I think one of the jiraph people (amalloy or ninjudd maybe) will have to help you with that |
| 17:59 | justin_smith | visof: is this your first clojure project? |
| 17:59 | amalloy | i already answered that question yesterday - jiraph uses core.match, a version that doesn't like being AOTed (is that fixed in latest core.match? i don't know), and you're AOTing everything |
| 18:00 | visof | justin_smith: i'm very new at clojure |
| 18:00 | amalloy | as for jiraph, like...we never really got around to making it user-friendly |
| 18:00 | amalloy | if you're not capable of contributing to jiraph, you're probably not capable of using it |
| 18:00 | amalloy | i would recommend something else for a first project |
| 18:01 | visof | so i can't use it for simple project? |
| 18:01 | visof | amalloy: can you help me please to setup a simple projecT? |
| 18:03 | amalloy | no, i can't. i really suggest you try something else |
| 18:03 | visof | amalloy: well, can you direct to me to start using jiraph? |
| 18:04 | visof | i want to use it very much |
| 18:04 | amalloy | considering all the pain it's put you through already, i can't imagine why |
| 18:16 | felher | Hey folks. I knnow I can use update-in to update values in a map. How would I go about updating things in a set in a similar manner? Maybe something like `(update-in-set set predicate update-function)`. |
| 18:17 | felher | Maybe I am just blind, but nothing in clojure.set seems to do the job. :) |
| 18:17 | ticking | felher: update-in is used for associative things like vectors or maps |
| 18:17 | mi6x3m-alt | felher: this is over-engineering |
| 18:17 | AeroNotix | felher: so you want to replace a value in a set with the f . v? |
| 18:17 | mi6x3m-alt | just use conj |
| 18:18 | AeroNotix | felher: conj won't remove the previous value |
| 18:18 | felher | Maybe I should provide an example: |
| 18:18 | AeroNotix | felher: definitely |
| 18:18 | AeroNotix | sounds like you |
| 18:19 | ticking | felher: (set (map #(if (pred %) (f %) %) myset)) |
| 18:19 | AeroNotix | you've chosen the wrong datatructure to me |
| 18:20 | ticking | felher: this is the easiest and "fastest", you won't get faster than O(n) because there is no index on pred. The reason update-in exists is because vectors and maps have indices so to speak.(in both the general sense as well as the database sense) |
| 18:21 | felher | Say I have a something similar to a relation database: #{ {:name 'x' :age 40 :salary 1000} { :name 'y' :age 50 :salary 1200} ... }. How would I go about updating the salary for everybody over 45 :) |
| 18:21 | mi6x3m-alt | felher: ah :) |
| 18:21 | felher | ticking: yeah, I know that i can't get better than O(n). The thing about map is, that I loose special sets. If I put in a sorted-set, i get a normal set out of it :) |
| 18:22 | justin_smith | felher: map with an if that either does an update-in or returns the original, than put the lazyseq that comes out of the map back into a set again |
| 18:22 | ticking | (sorted-set (... ? |
| 18:22 | AeroNotix | felher: clojure.core/map |
| 18:22 | justin_smith | ticking: oh yeah, maybe sorted-set-by :age if that is a common operation you want to optimize for |
| 18:23 | mi6x3m-alt | felher: do you have a copy of "Programming Clojure" around? |
| 18:23 | mi6x3m-alt | it provides a good overview of releational functions on sets |
| 18:23 | ticking | felher: sets are not relational databases even though core.set provides a lot of relational functions |
| 18:24 | mi6x3m-alt | select/project/join etc. |
| 18:24 | felher | yeah, the problem with just putting sorted-set before it, is that I don't know if it is created with sorted-set-by. |
| 18:24 | ticking | felher: what are you building? why is it relevant to return the same kind of set? |
| 18:24 | felher | mi6x3m-alt: yes, I do @ programming clojure) |
| 18:25 | mi6x3m-alt | felher: look towards the end of chapter 3 |
| 18:25 | mi6x3m-alt | "Calling Structure-Specific Functions" |
| 18:25 | mi6x3m-alt | your problem is one of relational sets |
| 18:25 | mi6x3m-alt | which is exactly what the chapter covers |
| 18:25 | ticking | mi6x3m-alt: core.set does not cover his problem |
| 18:25 | seangrov` | bbloom: Thanks for all the help, really excited now that the logical/render tree stuff is sorted, it seems to be working very decently |
| 18:25 | mi6x3m-alt | yes, the updating step will be ugly |
| 18:25 | ticking | mi6x3m-alt: I'd be suprised if the book covered it then |
| 18:26 | felher | ticking: because it gets serialized into json and I want to have the new json string as close to the old one as possible. Which requires the sets to be ordered. :) |
| 18:26 | mi6x3m-alt | ticking: it doesn't cover an update for sure :) |
| 18:26 | felher | mi6x3m-alt: okay, I will take a look at that. :) |
| 18:26 | ticking | y |
| 18:27 | justin_smith | felher: why a set rather than a vector or seq? |
| 18:27 | ticking | (inc justin_smith) |
| 18:27 | lazybot | ⇒ 39 |
| 18:29 | felher | justin_smith: actually, I probably would use a vector now. I realized that I needed a order quite late, when I already used sets. :) |
| 18:30 | felher | But if there isn't something like update-in, which takes care of using the same order for sorted things, it might just be the easiest thing to move away from sets and use vectors instead. :) |
| 18:32 | ticking | ist there documentation on the proper way to mix clojure and cljs namespaces in a library project? is cljsbuild required? So far I've seen everything from just adding cljs as a dep to having different profiles with clj and cljs. |
| 18:32 | ticking | without using cljx ^^ |
| 18:35 | felher | Well anyways, I think i'll tackle the refactoring from sets to vectors tomorrow and go and get some sleep now. Thanks all for your help :) |
| 18:40 | coventry | cljsbuild is not required, but it is super handy. |
| 18:42 | ticking | coventry: is it still possible to have different source-paths for clj and cljs then though? |
| 18:47 | jack_rabbit | is anyone familiar with the ring session middleware? |
| 18:48 | Frozenlock | ~ask |
| 18:48 | jack_rabbit | I need to get a session value in another piece of middleware I'm writing, but it doesn't appear, even when I put the session middleware before mine in the stack. |
| 18:48 | clojurebot | The Ask To Ask protocol wastes more bandwidth than any version of the Ask protocol, so just ask your question. |
| 18:48 | jack_rabbit | Frozenlock, indeed, sorry. |
| 18:48 | jack_rabbit | an empty session appears. Only in the actual route handler does the populated session map appear. |
| 18:49 | jack_rabbit | I'm using compojure, by the way. |
| 18:49 | Frozenlock | Is it really 'before'? (you tried both before and after?) |
| 18:50 | bbloom | seangrov`: cool |
| 18:50 | jack_rabbit | Yeah, as best as I can tell. |
| 18:50 | jack_rabbit | Where I expect it to be 'before', an empty :session {} key-value pair appears. |
| 18:50 | jack_rabbit | Where I expect it to be after, the key-value pair is not present in the request. |
| 18:50 | jack_rabbit | I've used macroexpand-1 to ensure I'm not crazy. |
| 18:51 | justin_smith | ,(sorted-set-by #(compare (:a %) (:a %2)) {:a 10 :b 2 :c 3} {:a 2 :b 5} {:a -1 :b 3} {:a 66 :c 8}) felher: also, do check out sorted-set-by |
| 18:51 | clojurebot | #{{:b 3, :a -1} {:b 5, :a 2} {:c 3, :b 2, :a 10} {:c 8, :a 66}} |
| 18:51 | seangrov` | bbloom: http://dl.dropbox.com/u/412963/Screenshots/ec.png running out now, btu would be good to talk with you about ideas for speeding up initial layout |
| 18:52 | justin_smith | jack_rabbit: where do you put a value in the session? |
| 18:52 | bbloom | seangrov`: how slow could it possibly be? :-P |
| 18:53 | justin_smith | jack_rabbit: it is totally empty until you put things in it, and it is persisted it by returning it in your response map {:body "hello" :headers {...} :session {...}} |
| 18:53 | jack_rabbit | justin_smith, in the route handler. When I check the session in another route handler, the map is populated properly. |
| 18:53 | jack_rabbit | justin_smith, yes, I've done that. |
| 18:53 | justin_smith | OK |
| 18:54 | justin_smith | on a second request, after the session has been populated, a middleware should see the values if it is active after wrap-session in the chain |
| 18:55 | justin_smith | also, remember that with the typical handler-chaining, it is possible to look at the request both before and after the next handler in line |
| 18:55 | justin_smith | handler middleware chaining that is |
| 18:57 | jack_rabbit | hmm... I'll play with the order. The handler is responsible for determining whether the user can perform certain actions, so I don't really want to propagate the request further if I can determine if it shouldn't be propagated. |
| 18:59 | justin_smith | yeah, seems like you would want it to be the first middleware in the chain after wrap-session |
| 19:00 | justin_smith | do you use the -> chain operator for describing your middleware? (-> handler middleware-1 middleware-2 ...) |
| 19:00 | justin_smith | with one per line that makes it easy to shuffle the order as needed |
| 19:00 | coventry | ticking: yes. It's probably what you want to do. I find cljs compilation sometimes slows down alot if I accidentally put a clj file in the cljs path. |
| 19:01 | ticking | coventry: ok thanks ^^ |
| 19:07 | jack_rabbit | justin_smith, yeah. I have (-> app-routes my-middleware ring-session ring-cookies) |
| 19:15 | justin_smith | jack_rabbit: I always get confused about the ordering, because any of the middleware could as easly do (fn [handler] (fn [request] (handler (f request)))) or (fn [handler] (fn [request] (f (handler request)))) and I always forget which is the default |
| 19:15 | justin_smith | or "normal" or whatever |
| 19:16 | justin_smith | I think that order is right though |
| 19:16 | jack_rabbit | Sure, but if they did their work after, the request handler wouldn't be able to see the results, no? |
| 19:16 | justin_smith | right, it is the difference between pre-processing and post-processing |
| 19:16 | jack_rabbit | For some it wouldn't matter. For sessions, I think it needs to be (handler (f request)) |
| 19:16 | justin_smith | right |
| 19:17 | justin_smith | good point |
| 19:17 | jack_rabbit | hmm. I'm reading wrap-session source. |
| 19:18 | jack_rabbit | It looks like it should be working correctly as I have it. |
| 19:22 | jack_rabbit | OH! |
| 19:22 | jack_rabbit | crap. |
| 19:22 | Glenjamin | typo on the key? |
| 19:23 | jack_rabbit | Do I need to return the session from all of my handlers now? |
| 19:23 | yedi | does anyone know why this guy might be calling (var) here? https://github.com/ptaoussanis/sente/blob/master/example-project/src/example/my_app.cljx#L112 |
| 19:24 | jack_rabbit | wait, no. That wouldn't explain it. |
| 19:24 | justin_smith | yedi: yeah, that way you can redefine the var and the new handler is being used |
| 19:24 | justin_smith | yedi: otherwise you need to restart the httpkit server to change the handler |
| 19:24 | justin_smith | (or do some other form of indirection) |
| 19:24 | justin_smith | it is a dev time thing |
| 19:25 | yedi | so if you redef the handler, httpkit will start serving based on the new handler? and without the var that wouldn't happen? |
| 19:25 | Glenjamin | http://http-kit.org/server.html#stop-server |
| 19:25 | justin_smith | yedi: yeah, if you just used the function name and not the var, it would dereference too early basically |
| 19:25 | justin_smith | so it would not see updates |
| 19:26 | justin_smith | by passing the var, you force it to deref on each request |
| 19:27 | justin_smith | jack_rabbit: yes, if you retern an empty session from any request, that client now has an empty session |
| 19:27 | justin_smith | jack_rabbit: I used to run into this bug often :) |
| 19:27 | justin_smith | the easy way to handle it is to return the request that came into the handler, with any extra stuff you want to add (and updates to session of course) |
| 19:28 | justin_smith | if all handlers are returning the request that came in, then anything that needs to persist, will, as the codebase evolves |
| 19:28 | jack_rabbit | justin_smith, yeah, unfortunately it doesn't explain my bug. My middleware's showing an empty session even on the one where my session is returned. |
| 19:28 | justin_smith | jack_rabbit: oh, very weird |
| 19:28 | justin_smith | sounds like it's time to do some tracing |
| 19:29 | jack_rabbit | yeah... unfortunately. |
| 19:29 | justin_smith | (def debug (atom {})) (swap! debug assoc (java.util.Date.) request) - just put that in a bunch of places in the code |
| 19:29 | justin_smith | then you see the traces at your leisure, in the repl |
| 19:30 | justin_smith | *can see |
| 19:30 | Glenjamin | looking at the source, omitting the :session key shouldn't update or delete anything |
| 19:30 | justin_smith | really? I could have sworn this has happened to me in the past |
| 19:30 | justin_smith | maybe the newer version is smarter |
| 19:30 | justin_smith | anyway, bbl |
| 19:53 | jasonjckn | if you have multiple consumers of a infinite lazy sequence, and one of the consumers is slower than the other, than this will cause an out of memory exception eventually as the segment of the sequence that is materialized in memory without being garbage collected grows over time unbounded |
| 19:53 | jasonjckn | does anyone know a solution to this problem, e.g. a lazy seq bounded on materialization |
| 19:53 | clojurebot | Excuse me? |
| 19:54 | bbloom | jasonjckn: if you're encountering that problem in practice you either A) have a bug or B) should be using something closer to core.async |
| 19:57 | jasonjckn | bbloom: so the solution is bounded queue/channels |
| 19:58 | jasonjckn | bbloom: so that the slowest consumer causes back pressure on the producer |
| 19:58 | bbloom | jasonjckn: precisely |
| 19:58 | jasonjckn | bbloom: that means all consumers will eventually consume at a rate of the slowest consumer |
| 19:59 | jasonjckn | that makes sense, I was hoping to preserve some element of the lazy seq, but i guess that's a lost cause, or just too complicated. |
| 19:59 | bbloom | jasonjckn: in many (most?) cases, that's just fine. usually one of two things happens: 1) the slower consumer occasionally falls behind and catches up... or 2) you optimize the slow consumer |
| 19:59 | jasonjckn | the lazy seq would then need to track cursors of each consumer, and ensure the spread of cursors doesn't get too wide |
| 19:59 | bbloom | if neither of those things apply to you, you need to handle dropped messages |
| 20:00 | bbloom | what property of lazy seqs do you need to preserve? |
| 20:01 | jasonjckn | well it's a long story, the short story is I don't, queues/channels will work just fine. I'll push it on other people at work. |
| 20:01 | bbloom | lol |
| 20:01 | bbloom | "somebody else's problem! *tosses over shoulder*" |
| 20:01 | bbloom | nice. |
| 20:02 | bbloom | if you're a single process app (which most ppl can get away with much much much longer than they think they can) then backpressure all the way to the level of individual external requests is the way to go |
| 20:02 | jasonjckn | yes absolutely |
| 20:02 | jasonjckn | i'll encorporate that |
| 20:02 | bbloom | if your consumer slows down and the buffer fills up, http requests start blocking on the queues... so the end user gets a slightly slower page load |
| 20:03 | bbloom | *shrug* oh well, you'll see that in your metric and optimize |
| 20:03 | bbloom | that's 100X better than most folks do... which is usually to crash and/or lose data.... so pat yourself on the back & worry about harder stuff later |
| 20:03 | jasonjckn | bbloom: what's your github |
| 20:03 | bbloom | jasonjckn: github.com/brandonbloom/ |
| 20:18 | nDuff | I have code compiled with CLJS 2202 throwing a TypeError in cljs.core when trying to set! cljs.core.PersistentQueue.EMPTY -- appears that its definition depends on cljs.core.PersistentArrayMap, which is undefined. |
| 20:22 | nDuff | PersistentArrayMap looks like it's defined in line 4287 of the original clojurescript / line 12013 of the generated javascript, whereas PersistentQueue.EMPTY is defined on line 3979 / 11541, so this looks like a genuine unmet dependency at first glance. What am I liable to be missing here? |
| 20:23 | gtrak | oiy, any knowledgeable tools.nrepl folks here? I think the middleware dependency mechanism is borked. |
| 20:23 | gtrak | or I'm pushing its limits somehow. |
| 20:34 | bstro9000 | in clojure.zip, is traversing through the zipper mutating the current "position" of the zipper? Or is it immutable. I.e, would excecuting (-> myzipper zip/down) twice return the same position in the data structure? |
| 20:35 | bbloom | bstro9000: it's immutable, that's kinda the point |
| 20:35 | bstro9000 | Cool. |
| 20:36 | bstro9000 | Is it possible to traverse a zipper in other ways than up/down/left/right? Or could I look for specific values inside a zipper. |
| 20:37 | bstro9000 | such as in a hash map. could i navigate to a specific key/value pair. |
| 20:39 | bbloom | bstro9000: the up/down/left/right business isn't super useful. but the clojure.zip/next is useful if you need a depth first traversal |
| 20:39 | bbloom | beyond that, it's generally preferable to use explicit paths with get-in, update-in, etc |
| 20:39 | bbloom | rather than zippers, i mean |
| 20:41 | bstro9000 | bbloom: Got it. Is it possible to emulate the functionality of zippers with what you're suggesting? I.e. the ability to pass a limited subset of data to a component, with that subset still knowing where it exists within the whole – thats the functionality i am looking for. |
| 20:41 | bbloom | bstro9000: does the function need to know where it is in the whole? or does the return value need to be put back in to where it came from? |
| 20:42 | bstro9000 | Either seem reasonable to me. |
| 20:42 | bbloom | are you familiar with update-in ? |
| 20:43 | bstro9000 | Not really. To be truthful, this is the first time I've ever used clojure's data structures. Coming from JS-land. |
| 20:44 | bbloom | ,(update-in {:some {:deep {:vector [:in {:a "map"}]}}} [:some :deep :vector 1 :a] #(.toUpperCase %)) |
| 20:44 | clojurebot | {:some {:deep {:vector [:in {:a "MAP"}]}}} |
| 20:45 | bbloom | lots of examples of update-in around the web, check em out |
| 20:45 | bstro9000 | Cool thanks man |
| 20:45 | bbloom | ,(update-in {:awesome-functions #{}} [:awesome-functions] conj :update-in |
| 20:45 | clojurebot | #<RuntimeException java.lang.RuntimeException: EOF while reading> |
| 20:45 | bbloom | ,(update-in {:awesome-functions #{}} [:awesome-functions] conj :update-in) |
| 20:45 | clojurebot | {:awesome-functions #{:update-in}} |
| 20:46 | bbloom | avoid zippers for now |
| 20:46 | bstro9000 | bbloom: any easy-to-explain reason for that? |
| 20:46 | bstro9000 | for avoiding zippers, that is. |
| 20:47 | bbloom | bstro9000: there's just not as general as you'd expect & it turns out that update-in and friends get the job done 95% of the time much more clearly |
| 20:48 | bstro9000 | bbloom: Got it. Thanks a bunch for yoru help. |
| 20:48 | bstro9000 | *you |
| 20:48 | bstro9000 | *your. |
| 21:32 | ToxicFrog | Is there a convenient way to (map) over the values of a map? |
| 21:33 | bbloom | ToxicFrog: do you want a map out at the end? |
| 21:34 | bbloom | ToxicFrog: if you just want to map over the values, you can easily (map f (vals m)) |
| 21:35 | bbloom | if you want a map w new keys, you need to do something like (into {} (map (fn [[k v]] [k (f v)]) m)) |
| 21:35 | ToxicFrog | bbloom: I want a map with the same keys and different values |
| 21:36 | ToxicFrog | That is basically what I'm already doing, but I was wondering if it already existed and I just don't know the name |
| 21:36 | bbloom | sadly not |
| 21:36 | ToxicFrog | Because I've already rolled my own versions of like three other library functions in this project and then later discovered they already existed |
| 21:51 | kras | ToxicFrog: typical clojure beginning :-) |
| 21:53 | kras | Are there any alternative clojure implementations specifically targetting compiling to machine code? Something like cython for python |
| 21:55 | gtrak | kras: closest thing is probably clojure-scheme->gambit-scheme->C->machine-code. |
| 21:55 | gtrak | https://github.com/takeoutweight/clojure-scheme |
| 22:00 | kras | gtrak: thanks, will take a look |
| 22:03 | ivan | kras: are you solving the startup time problem or the memory usage problem? |
| 22:04 | ivan | if it's just startup time I was going to suggest using Linux + CRIU to create and restore Clojure zygote processes |
| 22:04 | kras | ivan: its more to do with c/c++ interop |
| 22:05 | ivan | ah |
| 22:08 | kras | something like CFFI for CL http://common-lisp.net/project/cffi/manual/cffi-manual.html#Introduction |
| 22:14 | bbloom | is there a swap! variant that lets me return extra info? or do i need to hack my own on top of compare-and-set! plus a loop? |
| 22:14 | bbloom | or use an extra atom or something to write out to |
| 22:16 | bbloom | nevermind, seems like swap just does an infinite loop. i'll do that w/ a compare-and-set! |
| 22:17 | ddellacosta | bbloom: Would adding a watch independently of your swap! operation be a good workaround? You could probably get most any info you need that way. |
| 22:18 | bbloom | ddellacosta: the tricky bit is that i don't know where in the data structure to read/write unless i look at the data structure... but the data structure might change outside the swap fn body! compare-and-swap! in an otherwise infinite loop does the trick just fine |
| 22:19 | ddellacosta | bbloom: gotcha. |
| 22:21 | ToxicFrog | Oh what the hell |
| 22:22 | ToxicFrog | There's already a 'mapv' function for map over vectors?! |
| 22:24 | Cr8 | mapv is map that -returns- a vector |
| 22:25 | Cr8 | and, therefore, isn't lazy |
| 22:25 | Cr8 | its input coll can still be whatever |
| 23:03 | ToxicFrog | PSA for core.async users: if you have a channel that is (sub)d to a pubsub, and you don't want it anymore, you have to (unsub) from all of its topics as well as close!ing it. |
| 23:03 | ToxicFrog | Just close!ing it will result in irritating and hard to track down bugs as messages vanish. |
| 23:03 | ToxicFrog | Or possibly get stuck, I'm not sure! |
| 23:04 | ToxicFrog | I would have expected it to automatically unsub based on the description of (tap), but apparently not. |