#clojure logs

2014-05-04

00:35eg0anybody familiar with light table? cant save anything, get eacces permission denied
00:36eg0even in user directories
00:37eg0does it need to run as root?
01:03TEttingereg0: that's awfully odd
01:03TEttingeron windows?
01:04eg0TEttinger: ubuntu
01:04eg0reinstalling everything now
01:16itimmyAllow me to introduce myself, my name is itimmy and I was sent here from the gods
01:26satshabadI want something like an some-> except that in adition to a nil, I get an error message back too
01:26satshabadaddition*
01:26satshabadI... I think I want a Monad?
01:27satshabadCan I use maybe ..half a monad or something?
01:27amalloysatshabad: yeah, you kinda want Either. but hey, exceptions have been the special-case monad for that in java for years
01:28satshabadtry catch? hmm, I was kind of hoping for elegant tonight
01:29satshabadMaybe 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:30satshabadthis is interesting: http://adambard.com/blog/acceptable-error-handling-in-clojure/
01:40amalloysatshabad: that's literally just a hand-rolled implementation of Either
01:40satshabadhaha I know :)
01:40amalloywhich, okay, Either is pretty cool. but if you wanted to avoid monads...
01:41satshabadI 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:42eg0TEttinger: got things going
01:42TEttingerthat's good
01:42TEttingerwhat do you think caused it, eg0?
01:57MorgawrAre 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:06TEttingerMorgawr, that would be nice wouldn't it... I don't think it exists right now
02:06MorgawrTEttinger: a shame :(
02:07TEttingerthere might be a read-through of core.clj somewhere
02:07MorgawrI would love for somebody to create a Clojure dialect that runs on rust
02:07TEttingerbut really what you're interested in is the Java side, or JS side?
02:07TEttingeror LLVM!
02:07Morgawrrust is on llvm
02:07Morgawrso yeah
02:08MorgawrI'm interested in the process of building a clojure language that runs on <whatever platform>
02:08Morgawrlike, 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:09Morgawrbut 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:09TEttingerpeople seem to be implementing kernel a lot
02:09TEttingerhttp://axisofeval.blogspot.com/2011/09/kernel-underground.html
02:10TEttingerlisp-like language
02:10MorgawrTEttinger: nice
02:11TEttingermight be a good place to start, Wat was 400 lines of JS at one point IIRC
02:11TEttinger(and could eval at that point)
02:12Morgawrhttps://github.com/brandonbloom/eclj this seems interesting
02:14TEttingerwoah
02:15TEttingerbbloom, that's slick
02:15Morgawroh, it's from bbloom
02:15Morgawrneat
02:17Morgawrhttp://www.infoq.com/presentations/Clojure-LLVM/ this is also probably worth watching
02:18eg0oooo thats awesome
03:39ivanis there a way to keep track of aliases in the user namespace and ns-unalias+alias them after a tools.namespace refresh?
03:49ivanoh ns-aliases
03:52quizdrany clojurescript users in here at the moment? I have a question on compiling into the js file
04:00Morgawrquizdr: just ask your question and people might answer :)
04:06quizdrWell, 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:08Morgawrquizdr: 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:09Morgawrand if you don't have optimiations (google closure's pruning, etc etc) it's going to be quite lengthy of course
04:09Morgawrand 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:10Morgawrthere is a clojurescript compiler if I remember correctly but everything is abstracted away thanks to lein so I don't know more
04:10quizdrinteresting, 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:11MorgawrI 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:11TEttingerquizdr, it is in java too. that's what the dependency on clojure.jar is
04:11Morgawrbut 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:11TEttinger(clojure.jar has all of the stuff in core.clj and related ns's)
04:12TEttingerit might be good to investigate what's in the jar that lein downloads, Morgawr
04:13quizdrWhat 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:13MorgawrTEttinger: definitely
04:13MorgawrI mean, I know about clojure.jar, I just wasn't sure about javascript
04:14Morgawrquizdr: http://stackoverflow.com/questions/19963948/how-to-set-the-clojurescript-print-fn-fn-in-nodejs this might be useful
04:14MorgawrI usually use (.log js/console ...) in clojurescript
04:14ptcekIs there any way to define records with defaults (other than macros like http://cemerick.com/2010/08/02/defrecord-slot-defaults/)
04:15TEttingerMorgawr: yep, the jar that lein uses has not only clojure.core, but also several other ns's I didn't know came with
04:16TEttingerpprint, zip, xml
04:16gunsptcek: I think a custom ->Record constructor is the best answer. A little verbose but explicit
04:17MorgawrI usually write a myrecord-init function for all my defrecords
04:17Morgawrthat macro linked might turn out useful (but I dislike the name)
04:18gunsIt's sometimes useful to create records with implicit fields (like timestamps). that's just more macro magic of course
04:19Morgawron 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:19Morgawrand I mean, I got a critical boost in performance from such a change in my code
04:19gunswhat kind of % delta?
04:19Morgawroriginal code ran in 12seconds, with the record re-instantiation it ran in 6-7 seconds
04:20Morgawrso, cut down the run time in half
04:20gunspretty big. I guess the ctor doesn't even bother with the IPersistentMap interface
04:20gunsjust a guess
04:20MorgawrI 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:21gunsoh yeah, that should be faster
04:21Morgawrgood to know :)
04:21gunsassoc with multiple values just does a simple reduce
04:21Morgawrah
04:21Morgawrso it's building several layers?
04:21gunsit does the persistent coll thing
04:22quizdrSo 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:23afhammadgiven a vector of maps with :id's what is best/fastest way to insert a new map after map with :id x
04:23afhammadI realize the vectors might not be best suited for this operation, open to suggestions
04:24Morgawrquizdr: 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:24Morgawrafhammad: why not use a map of maps and store the id as key?
04:25Morgawrlike { 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:26gunsAnd if all the :id values are positive ints, you can use the new immutable-int-map library for performance
04:26Morgawrguns: oh, never heard of that, got any resources? it might turn out to be useful for one of my projects
04:27gunsMorgawr: https://github.com/ztellman/immutable-int-map
04:27gunsIt screams "build a trie"
04:28Morgawraw, it uses parallel threads.. cool but it's not going to work for me
04:28afhammadFYI this is in ClojureScript
04:28Morgawrstill an interesting library
04:28Morgawrafhammad: shouldn't matter
04:29gunsMorgawr: It only mentions parallel threads in the discussion about the reducers library
04:29gunsit is totally concurrency agnostic
04:29Morgawrah
04:29Morgawrso it doesn't use threads in the implementation, good good
04:29MorgawrI misunderstood the readme then
04:29afhammadalso I need to traverse the collection sequentially as it is being rendered as dom elements
04:30gunsit just so happens to be very good at merging
04:30Morgawrafhammad: you can get a list out of a map using the vals function
04:30Morgawrnot sure if it's faster/slower though
04:31afhammadok, will look into it. Thanks guys
04:36Morgawrhttps://code.google.com/p/jvmtop/ just found out about this, pretty cool tool
04:36Morgawrthought it'd share
04:38gunsMorgawr: oh that is useful! It's nicer than htop showing a 100 "java" threads
04:38Morgawrguns: yeah, exactly
05:13yotsovis 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:30martinklepschhey. 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:30martinklepschhttps://www.refheap.com/85084
05:31martinklepschI wonder if an easier way might be to write a function that converts this whole thing to a hash map (omitting attributes)
05:32martinklepsch(just noticing that this wouldn't work with <b><a>hello</a><a>world</a></b>)
05:36TEttingermartinklepsch, I can see why you'd want to parse that patent in particular, that's hilarious
05:36martinklepschTEttinger, novelty jeans for the win!
05:37TEttingerso you need to parse malformed HTML?
05:37TEttingererr XML
05:38TEttingerI guess that example wasn't malformed
05:39TEttingerso what kind of data are you trying to get out of it, martinklepsch?
05:40martinklepschTEttinger, yeah. already doing that using xml-zippers but the API feels clumsy (maybe because I didn't understand it well enough too)
05:40MorgawrI've been reading Clojure High Performance Programming and wow it is a good book
05:41MorgawrI strongly recommend it
05:41martinklepschTEttinger, right now I want to build a UID by the stuff thats in the document-id tag
05:41TEttingerI uh... wouldn't expect to hear that that book exists
05:42MorgawrTEttinger: 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:42Morgawrreally a good read
05:42Morgawrand it's also relatively short too, only 150ish pages
05:42TEttingerMorgawr, will you do the hard work and optimize Cloister for users like maybe me?
05:43MorgawrTEttinger: yeah, this is part of why I'm reading this book :)
05:43MorgawrI'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:44Morgawrand with the knowledge I gain I hope I can work more on Cloister during the summer
05:44TEttingermartinklepsch: honestly the simplest possible way I can think of is to hash the contents of that document-id
05:44TEttinger(might want to not use a JVM hash function, use your own)
05:50TEttingerneat, clojure MD5 that shouldn't change on JVM updates. http://zackmdavis.net/blog/2014/01/consistent-hashing/
05:51xecycleDoes the repl completion support Java-only classes?
06:00martinklepschTEttinger, 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:00TEttingerindeed...
06:02penthiefcider-jack-in (v0.6.0alpha) doesn't seem to use :repl-options like "lein repl" does. Am I missing something?
06:26martinklepschparsing 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:30martinklepschTEttinger, ^ somewhat related to my previous question, if you're curious
06:30TEttingerI actually have no idea how to use the clojure zippers, sorry
06:34martinklepschTEttinger, has nothing to do with clojure zippers actually
06:35Kneivaupdating 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:38Kneivaany idea what's wrong?
06:39martinklepschKneiva, seems like a dependency issue?
06:39martinklepschhttp://www.slf4j.org/codes.html#StaticLoggerBinder
06:39Morgawrquestion: 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:39Kneivathat has been there before
06:40Morgawras in, are agent creations also buffered inside a transaction?
06:40Kneivamartinklepsch: that ns_tracker stackoverflow is the new thing
06:43AeroNotixMorgawr: try it?
06:44MorgawrAeroNotix: 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:46AeroNotixMorgawr: use a ref with two transactions, one transaction completes quickly (e.g. 50ms) and the other slow (e.g. 500ms)
06:46MorgawrI tried this
06:46Morgawr(dorun (pmap (fn [_] (dosync (alter r inc) (let [b (agent 0)] (send b (fn [_] (swap! x inc)))))) (range 1 10000)))
06:46Morgawrwhere r is a ref and x is an atom
06:46Morgawrand if @r == @x then it works
06:46Morgawrand it does workb
06:47Morgawrbut not sure about retries and contention
06:47Morgawr(cause there's none)
06:47Morgawris there any way to force retrying a transaction?
06:48AeroNotixMake two transactions
06:48AeroNotix(do (future (dosync ...)) (future (dosync)))
06:54Morgawr(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:54Morgawrthis doesn't seem to be working
06:55Morgawrx is 0 :|
06:55Morgawr(f and r are refs, x is an atom)
06:56AeroNotixwhat do you mean "not working" ?
06:56Morgawralso it doesn't make sense, f and r should both be 0 but they become -99 and 99
06:56MorgawrAeroNotix: I mean I'm getting weird results
06:56AeroNotixMorgawr: explain
06:57dissipatewhat happens if you use memoize on a non-referentially transparent function?
06:57Morgawronly 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:58Morgawrand on top of that the atom is never modified
07:00AeroNotixdissipate: sadness happens
07:00AeroNotixMorgawr: show a full example
07:00amalloyMorgawr: 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:01Morgawramalloy: oh.. derp I used x as name for the doseq counter.... that's why
07:01Morgawrand I fucked up the syntax
07:01Morgawrwelp
07:03Morgawrokay 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:03Morgawrunless I messed up something else too
07:03dissipateAeroNotix, 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:03Kneivahmm, changing back to 1.5.1 does not remove the problem
07:06AeroNotixdissipate: try it !
07:06dissipateamalloy, is it common for programmers to abuse atoms? :(
07:06AeroNotixDo people forget they have a REPL
07:06Morgawrokay, 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:08dissipateAeroNotix, 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:10Morgawrdissipate: 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:10Morgawrand if on a subsequient function call that key/val pair is found then it returns the result alread in the map
07:10Morgawrelse it runs the function
07:10Morgawrso it makes sense that the function is only run once (with its side effects or whatever)
07:11dissipateMorgawr, 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:12Morgawrdissipate: https://en.wikipedia.org/wiki/Memoization this?
07:14dissipateMorgawr, "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:15dissipateMorgawr, 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:15Morgawrhow can you check if a function is non-referentially transparent? it means it ought to have no side effects
07:15Morgawrand then we enter into haskell's domain
07:16Morgawrwhat 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:17AeroNotixdissipate: it's still a non-referentially transparent function, just because you cache the results doesn't give it a separate term
07:18dissipateMorgawr, hmm, that's an interesting question. is there a test you can apply to detect if a function is not pure?
07:19dissipateAeroNotix, sounds correct. and so it is probably considered bad practice to memoize a non-pure function?
07:19Morgawrdissipate: 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:22dissipateMorgawr, 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:25AeroNotixdissipate: you can
07:25AeroNotixwrap code in (io!)
07:25AeroNotixthis is used in dosync blocks
07:26AeroNotixClojure 1.6.0
07:26AeroNotixuser=> (defn foo [] (io! (println "foo")))
07:26AeroNotix#'user/foo
07:26AeroNotixuser=> (dosync (foo))
07:26AeroNotixIllegalStateException I/O in transaction user/foo (NO_SOURCE_FILE:1)
07:26AeroNotixdissipate: Perhaps memoize could use similar semantics
07:27Morgawrthis is only for I/O though, not for all types of impurity
07:27AeroNotixMorgawr: what other types are there?
07:27Morgawranything that has side effects
07:27Morgawrlike swapping on an atom for example
07:27Kneivamartinklepsch: 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:27AeroNotixMorgawr: sure
07:28dissipateAeroNotix, but the programmer has to use io!
07:28AeroNotixdissipate: sure
07:28martinklepschKneiva, that was indeed hard to see :D
07:29Morgawrdissipate: 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:29AeroNotixsigh
07:29Morgawrand the other would be to run the code and detect if it tries to access some "unsafe" memory areas
07:29Morgawrtainted areas or whatever
07:29AeroNotixdouble sigh
07:29Morgawrbut in any way, that's outside the scope of clojure and it's a big research problem
07:31Morgawr4
07:32Morgawrwhops, sorry
07:32dissipateMorgawr, i thought the problem had been solved. in Haskell everything is pure, with side effects relegated to monads, no?
07:32Morgawrdissipate: that's at compile time though
07:32Morgawrnot runtime
07:34dissipateMorgawr, so you are saying it's possible to have side effects outside of monads in Haskell at runtime?
07:34AeroNotixlol
07:35Morgawryour whole pc is a box full of side effects :P
07:35Morgawrbut 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:35Morgawr(although haskell does have unchecked exceptions -I think- and unsafe blocks)
07:41dissipateMorgawr, as i suspected, memoize is using swap!. that being the case, i'm wondering why it's not memoize! instead
07:42Morgawrfor the same reason why alter is not alter! or send is not send!
07:42Morgawralso memoize is using an atom internally, the developer doesn't really need to know
07:43dissipateMorgawr, 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:44TEttingerI think the ! is more for destructive in-place modification
07:44TEttingerprobably wrong
07:44MorgawrI 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:45MorgawrI'd use ! in every reftype operation just to have consistency but apparently this is not the case, so idk
07:45dissipateMorgawr, 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:46Morgawrno, my argument is that it's memoize because somebody decided so, I don't know about the actual reasoning
07:47Morgawrand yes, since the atom is just internal implementation it doesn't really matter because it's never exposed
07:47TEttingerah!
07:47TEttingerUse the bang! only for things not safe in an STM transaction.
07:47TEttingerhttp://dev.clojure.org/display/community/Library+Coding+Standards
07:48MorgawrTEttinger: makes sense
07:48dissipateTEttinger, 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:48dissipateTEttinger, but that raises the question. is it safe to memoize a function within a transaction?
07:49dissipateor rather to call a memoized function within a transaction
07:49Morgawrdissipate: that's a good question, considering the behavior (the memoized function should be referentially transparent), I'd say yes
07:49TEttingerall I gotta say is question mark
07:49Morgawreven in the worst case, a transaction is retried and the function is called again
07:49Morgawrbut instead it already has the cached value
07:49TEttingerhttp://www.badlogicgames.com/wordpress/?p=3428 time to make a lein plugin for this?
07:50Morgawrfor play-clj?
07:51TEttingerno, for in general
07:51dissipateMorgawr, hmm. and the transaction isn't going to detect the swap! occurring in the memoized function and then throw an exception?
07:51MorgawrTEttinger: ah, I only read the title...but I see now, cool stuff
07:52Morgawrdissipate: no, you can use swap! and other side-effecting functions inside a transaction, you shouldn't but you can
07:52Morgawrif you do, just make sure they are idempotent and don't create problem when retried, which is the case of memoized functions
07:52Morgawrs/of/for/
07:53dissipatehmm, i'm not sure if that's good. it means the judgement is left to the programmer
07:54Morgawrit's definitely not a good thing to do
07:54Morgawrthe 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:55Morgawrwhat 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:56MorgawrI do it to query requests to the audio engine from any thread without fear of replaying the request
07:56Morgawr(like play/pause/stop audio)
07:56Morgawrhttps://github.com/Morgawr/Cloister-Engine/blob/master/src/cloister/sound.clj#L191 <- this
07:56Morgawrit's not the best but it seems to work
07:56AeroNotixI'm sure I've seen something that is guaranteed to do things once
07:57AeroNotixI'm afk at the moment
07:57AeroNotix,(once-only)
07:57clojurebot#<CompilerException java.lang.RuntimeException: Unable to resolve symbol: once-only in this context, compiling:(NO_SOURCE_PATH:0:0)>
07:57Morgawrthat's for macros
07:57AeroNotix,once-only
07:57clojurebot#<CompilerException java.lang.RuntimeException: Unable to resolve symbol: once-only in this context, compiling:(NO_SOURCE_PATH:0:0)>
07:57AeroNotixah yes
07:58dissipateMorgawr, i see, interesting
08:02dissipateMorgawr, by using the agent in the transaction there is no possibility for a retry?
08:02amatsuIs there an official #clojure pastebin?
08:02Morgawramatsu: refheap.com but not sure how "official" it is
08:03Morgawrdissipate: 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:03Morgawrso in case of retries they aren't sent
08:04amatsuOK. So I was wondering whether this is particularly idiomatic Clojure https://www.refheap.com/85088
08:04dissipateMorgawr, and why is that considered to be a 'hack'? sounds good to me
08:05Morgawrdissipate: 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:06TEttingerthat looks perfectly reasonable, amatsu
08:07amatsuTEttinger: alright, thanks!
08:07dissipateMorgawr, are you trying to make Cloister-Engine *the* game engine for clojure?
08:07TEttingerI should ask, amatsu, did the transient code work the first time? I have had such bad luck with transients!
08:07Morgawrnah, I'm trying to make cloister-engine a usable game engine
08:08Morgawrplay-clj is probably a better option
08:08dissipateMorgawr, what kinds of games is it going to support?
08:08Morgawrmy project is just an experiment to see how it goes with a lot of threads and semi-uncoordinated entities in a game
08:08Morgawr2D games
08:08Morgawrit's still far from being done
08:08Morgawrcurrently working on the physics engine which is tricky because it's a non-threadsafe java engine and I'm wrapping around it
08:09dissipatewow, the physics namespace is pretty short
08:09amatsuTEttinger: 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:09amatsuTEttinger: once I figured that out it worked perfectly.
08:09Morgawrdissipate: well, it's incomplete :P
08:10MorgawrI mean, it runs physics thread but there's still no function to set physical bodies yet lol
08:10dissipateMorgawr, i see. i'm doing a game thing myself. but it's games for programmed bots, not human players.
08:10Morgawrdissipate: ah, cool
08:13TEttingerdissipate, what's that used for, AI research?
08:14dissipateMorgawr, 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:14Morgawrdissipate: good luck then :) Clojure is a great language
08:14dissipateTEttinger, yep, pretty much. but with a twist.
08:17dissipateTEttinger, 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:21dissipateTEttinger, more info here https://github.com/dissipate/matching-pennies-test
09:49akazlouhi, 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:50TEttingerI think that's a special property of into, but I'm not sure
09:51akazloualso (conj {} [1 2]), but this one is mentioned here http://clojure.org/data_structures on the Maps section
09:51TEttingererr yes I messed up
09:51TEttingerinto uses conj internally
09:52akazlouo, nice
09:52TEttingerinto is equivalent to (fn [to from] (reduce conj to from))
09:52TEttingerfor non-editable collections, anyway
09:52TEttingerfor editable ones it uses transients
09:56akazlouall right, read the doc of into once again, it is mentioned that the items are conjoined, so that is why, question is solved :)
09:56akazlouthanks TEttinger
09:57TEttingerheh no prob
09:57TEttingerit's usually more active here... maybe the san francisco people aren't up yet :-)
09:58akazlou:) np, as far as there are people who can help
10:20caulagiI want to do - (-> (add "joe" 2) (add "mary" 3))
10:20caulagiWhy is this wrong? (defn add [student grade] {})
10:20caulagiArityException Wrong number of args (3) passed to: user$add clojure.lang.AFn.throwArity
10:21ptcek-> macro adds the result of previous form as the second element in the next form
10:22ptcekthus the second form after -> expands to (add {} "marry" 3)
10:23ptcekcaulagi: what do you want to achieve?
10:27caulagiptcek: I am trying the problems at exercism.io. Can you please look at http://pastie.org/9139521?
10:28caulagithe add function still gives the arity exception
10:34TEttingercaulagi, it looks like exercism.io is not public. can you tell us what you are trying to do on this task?
10:35martinklepschare there any alternatives to enlive for scraping that one should be aware of?
10:36ptcekmartinklepsch: laser ?
10:37caulagiptcek, TEttinger: https://github.com/caulagi/exercism/tree/master/clojure/grade-school
10:37caulagiI 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:39caulagiI am confused with the use -> for the tests. Shouldn't my function definition here work? http://pastie.org/9139521
10:40TEttingerso db is a hashmap, let's say. {} is how it is defined
10:41ptcek(defn add [db student grade] ...) must work
10:41TEttingerwhen you use -> , it takes the first arg to -> , which starts as {} there, and makes it the first arg passed to add
10:42TEttingerso 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:43TEttingerand it should return a map
10:44caulagiTEttinger, ptcek: got it. I have to use db as the first arg for all my functions. Thanks!
10:44TEttingerno prob
10:45ptcekcaulagi: fyi, ->> is works the same but inserts the result of previous form as the last arg of the next form, quite useful too...
10:46caulagiptcek: is ->> more idiomatic?
10:47ptcekno, the same
10:49caulagiSo -> is the way to chain functions together, right? With the output from the previous function serving as the first input for the next?
10:51ptcekright, and ->> inserts the previous result as last argument
10:53Morgawrhttps://github.com/rplevy/swiss-arrows there's also this
10:53MorgawrI love the diamond wand
10:53Morgawrit should be part of clojure.core imo :P
11:03Glenjamin"-<><:p"
11:03Glenjaminthat seems like a joke.
11:11`szxMorgawr: isn't the diamond wand the same as as-> ?
11:13Glenjamini think so, with <> instead of explicit naming
11:22bbloomMorgawr: 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:22bbloomalthough the interpreter does have first class environments
11:56martinklepschis there no such thing as select1 in enlive?
12:35sritchiehey guys - is there a mapcat equivalent for core async? meaning, take a function of channel and item -> channel that returns a new channel?
12:36bbloomsritchie: https://github.com/clojure/core.async/blob/master/src/main/clojure/clojure/core/async.clj#L524-L552
12:37sritchiebbloom: that takes a function of item => collection
12:37sritchieI want something that takes a function of item => new channel of item
12:37sritchiemonadic bind
12:38bbloomsritchie: those mapcat functions do not produce or consume collections, they operate on channels
12:38sritchie" f must return a
12:38bbloomsritchie: it's the f function that returns collections
12:38sritchie collection."
12:38sritchieexactly - my f returns a channel
12:39sritchieI have a function that takes a stripe event id and returns a channel of a stripe event
12:39sritchieand I have a channel of stripe IDs that come in from a webhook
12:39sritchieso I want to take that channel and that function and return a new channel that produces stripe events
12:40bbloomsritchie: why not parameterize your code with a channel to report back to?
12:40sritchiesure, I can do that, or I can use "pipe" in core.async, looks like
12:40sritchiebut what I'm describing is the monadic bind,
12:41sritchiewhich has some pretty big advantages with the operators you can build on top of it
12:41bbloomsritchie: i'm quite familar with monadic bind, but you haven't specified for which monad :-P
12:41sritchiethe channel monad
12:41bbloomnot a thing.
12:42sritchiewhy not? It's like the Future monad
12:42sritchieI can write a bind and a return
12:42bbloomyou're talking about a stream monad of sorts on an individual channel
12:42sritchiethat's correct
12:42bbloombut close & multiplexing and other aspects of CSP means that monad doesn't really exist, as far as i can tell
12:43bbloomyou can overlay one on a subset of the functionality of channels
12:43sritchiewhich would still be helpful in this case
12:43bbloomit might be, but i suspect that you're overlooking a simpler solution :-)
12:45sritchiebbloom: how about https://gist.github.com/sritchie/fddf0c13708f651ef8c3
12:46bbloomsritchie: 1) you need to return the out channel
12:46martinklepschis there no such thing as select1 in enlive?
12:46sritchieoh, whoops
12:46bbloomsritchie: 2) you're not handling what hapepns if chan is closed
12:46bbloom3) should call that argument "in" as is the convention now
12:47bbloom4) that's map, not flatmap, you need an inner loop to accomplish a flatmap
12:48bbloomsritchie: to do what you want to do, you can just map pipe
12:54roelofhello, why do I see a dependency cycle on this script : http://pastebin.com/5Pk0XpvX
12:55bbloomroelof: def is for toplevels
12:55bbloomroelof: use let
12:55bbloomroelof: actually, you have quite a few errors in there
12:55bbloomroelof: your'e not actually calling ==
12:55sritchiebbloom: that actually seems to work
12:55sritchiewith your changes
12:55sritchiebbloom: it is in fact flatmap
12:55sritchiebind, rather
12:55sritchieI don't want to worry about sequences inside
12:56sritchieI can handle that with mapcat and into
12:56roelofbbloom : still the same error
12:56sritchiebbloom: https://gist.github.com/sritchie/fddf0c13708f651ef8c3
12:56roelofand I know there could be more errors . I try to figure out if it's working
12:57bbloomroelof: 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:57sritchieoh, wait, I see what you mean
12:57sritchiebbloom: yeah, since we can't just take one thing from each channel, sure
12:57bbloomroelof: it may be best to try something like instarepl in LightTable to tinker with clojure more interactively on smaller expressions
12:58Glenjaminroelof: what guide are you using to learn?
12:58roelofoke, I type it into the editor in intelij idea with the cursive plugin and I send it to the REPL with crtl+enter
12:59bbloomroelof: 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:59bbloomroelof: try to evaluate something simple like (+ 2 2)
12:59roelofI use two guides : clojure for the brave and i use Iloveponies github and do the 4clojure exercises
12:59bbloomsritchie: 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:00bbloomsritchie: see https://github.com/brandonbloom/asyncx
13:00sritchieso you think the way is to modify these stripe functions to optionally accept a source channel?
13:00sritchieand sequence that way
13:00bbloomi think that they should EXPLICITLY, not optionally, take a channel on which to report their results
13:02sritchieany killer example of why this flatmap pattern is so bad?
13:02sritchiefor this subset of core.async functionality?
13:04bbloomit's not flatmap that's bad
13:04roelofbbloom : I think it s a cursive problem. Even with a new project ( + 2 2 ) gives the same error
13:04bbloomroelof: i strongly recommend light table for learning
13:04bbloomroelof: instarepl is your friend
13:04bbloomroelof: it "just works"
13:05bbloomsritchie: the issue is that CSP exists to coordinate independent processes & just happens to not be totally useless for stream processing
13:05bbloomsritchie: that doesn't mean you should force everything in to a stream processing problem
13:05roelofoke, 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:05roelofthat why I do not use light table
13:05bbloomsritchie: if you genuinely have a very linear stream, there are much better approaches
13:06sritchiethe 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:06bbloomroelof: i have no idea what "sits on github" means and sending stuff to the repl is entirely automatic in instarepl
13:06sritchiebbloom: 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:07roelofbbloom : I do these lessons : http://iloveponies.github.io/120-hour-epic-sax-marathon/structured-data.html
13:08TravisDlol, that url.
13:08roelofhow I can make them work on light table and how I do lein midje so I can see if I do it right
13:08bbloomsritchie: argh. ok, so i'm not sure whether i should help you with your current problem or talk you out of webhooks lol
13:09sritchiehaha, nice
13:09sritchiebbloom: sure, talk me out of them
13:09bbloomsritchie: what happens if your server is down? you're going to need to implement batch synching anyway
13:09bbloomstart with that
13:09bbloomdon't start with webhooks
13:09bbloomb/c you dont strictly need that
13:09bbloomjust start with a cron job that fetches everything since the last cron job & does a batch process
13:10roelofTravisD: why lol. is it a bad way to learn clojure ?
13:10sritchiebbloom: yeah, fair
13:10bbloomsritchie: after that, only use webhooks to accomplish realtime goals
13:10bbloomsritchie: but chances are cron every minute will be good enough
13:11TravisDroelof: 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:12sritchiebbloom: I was going to go this road for mailing list unsubscribes,
13:12sritchiesince mandrill doesn't expose an API to check on unsubscribes that have occurred recently
13:13sritchiebbloom: I hear you on doing batch processing first
13:15sritchiebbloom: 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:16bbloomsritchie: http://help.mandrill.com/entries/22285907-What-happens-if-my-webhook-URL-is-down-or-can-t-accept-requests- *cringe* 100 retries lol
13:16cbpHow do I delete projects on clojars
13:16bbloomsritchie: presumably you can get the list of all subscribed people right? i'd do that and take set difference
13:18bbloomsritchie: 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:19bbloomsritchie: 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:19bbloomlike runState or whatever in haskell, you simply don't have access to that thing in core.async
13:19sritchieokay, sure
13:20bbloombut yeah, every time i've ever used webhooks, i greatly regretted it :-P
13:23sritchiebbloom: ugh, yeah. If only the real world didn't intrude :)
13:27mi6x3mhey clojure, aside from the creation step, what is the difference between def and alter-var-root
13:29bbloommi6x3m: alter-var-root takes a function of the current var root
13:29mi6x3mbbloom: yeah i'm clear on this, but is this all?
13:29bbloommi6x3m: def is a special form and has non-standard evaluation. alter-var-root is just a normal function
13:30mi6x3mOK, I see, but the net effect is more or less the same?
13:31mi6x3mchanging the root binding
13:31roelofCan anyone help me with a workflow that works with that url ???
13:31lazybotroelof: Yes, 100% for sure.
13:32roelofWhat is then a good workflow that works with that url and light table ??
13:32lazybotroelof: Definitely not.
13:32sritchiebbloom: so cron it is, I guess. back into writing deploy code
13:32bbloommi6x3m: the second call to (def foo x) is equiv to the first call of (alter-var-root #'foo (constantly x))
13:32bbloom(inc cron) ; just for you sritchie!
13:32lazybot⇒ 1
13:33bbloommi6x3m: what are you trying to accomplish?
13:33sritchie:)
13:33mi6x3mbbloom: absolutely nothing, just getting a clear picture on this :)
13:34bbloommi6x3m: see the intern and alter functions: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Var.java
13:34bbloommi6x3m: def is the sepcial form version of ##(doc intern)
13:34lazybotjava.lang.SecurityException: You tripped the alarm! intern is bad!
13:34bbloom(doc intern) ; stupid lazybot
13:34clojurebot"([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:35roelofbbloom : can you help me with a good workflow on the github page and light table ?
13:36bbloomroelof: 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:37roelofbbloom : I try to figure out how I can make that github page avaible in Light table ?
13:37bbloomroelof: copy/past
13:37bbloomi have no idea what "make available" means
13:37bbloomuse copy paste for code fragments
13:38bbloomsheesh
13:38roelofthanks. I will try that
13:46seangrov`bbloom: I hadn't realized that Haiku has been using ALM, pretty cool (along with their editor for it, ALE)
13:47bbloomseangrov`: yeah, spotted that at somepoint, but i don't know jack about haiku
13:47bbloomother than it's some approximation of a smalltalk environment written in c++
13:47bbloom... kinda like Microsoft Office ;-)
13:48seangrov`I think it's a rewrite of BeOS with document-oriented GUIs (not sure what that means though)
13:49seangrov`Ah, I see. It means that GUI's are serializable like documents, and editable by end-users
13:50bbloomseangrov`: yeah, it's got an ABI similar to Window's COM
13:50bbloomhence the office joke
13:51seangrov`Yeah, I don't think I would have got that joke ;)
13:53bbloomyeah, ppl don't realize that modern windows development is very smalltalk like... thanks to COM/OLE
13:53bbloombut smalltalk like with a shitload of C++ cruft that makes you want to cry
13:53bbloomand looks nothing like smalltalk, sadly lol
13:53bbloombut that's my (best available) understanding of beos too
13:53seangrov`Well, for better or for worse... MS knows their developer audience :P
13:53bbloomtrue story
13:53seangrov`I'm surprised they were able to sell the switch to html5/js internally
13:54bbloommy understanding is that they didn't so much sell it as shove it down an entire orgs throat
13:55Glenjaminis there a builtin (def sum (partial reduce +)) ?
13:56seangrov`,(reduce + 0 [1 2 3 4 nil 1 2 3 4])
13:56clojurebot#<NullPointerException java.lang.NullPointerException>
13:56Glenjaminclearly the definition is trivial, but i feel like most stdlibs with a reduce also include sum
13:56seangrov`,(reduce (fnil + 0) [1 2 3 4 nil 1 2 3 4])
13:56clojurebot#<NullPointerException java.lang.NullPointerException>
13:57seangrov`,(reduce (fnil + 0 0) [1 2 3 4 nil 1 2 3 4])
13:57clojurebot20
13:57seangrov`There we go...
14:20TEttingerGlenjamin, core.matrix has it, and a lot of other non-scalar operations
14:20TEttinger(IIRC)
14:21Glenjaminok, cheers - just wanted to check i hadn't missed something obvious
14:21TEttingerit's easy enough with reduce + or apply +
14:25Glenjaminyeah, i've just def-ed sum to improve readability in a big function a bit
14:36jdeisenbergHi. I'm getting the impression from my reading that vectors are preferable to lists for most cases.
14:36jdeisenbergis this accurate?
14:37bbloomjdeisenberg: yes
14:37bbloomjdeisenberg: it's worth understanding them though
14:37bbloomi mean the differences between the two
14:37bbloom~colls
14:37clojurebotcolls is seqs and colls
14:37bbloom~seqs
14:37clojurebotseqs is http://www.brainonfire.net/files/seqs-and-colls/main.html
14:37bbloomjdeisenberg: ^^
14:38jdeisenbergThanks; I'll read it.
14:40jdeisenbergHm. sequential? and seq? are two entirely different things.
14:41bbloomjdeisenberg: "does this object represent a sequential progression of values?" vs "does this object provide first/next lazy sequence operations?"
14:42bbloomjdeisenberg: the former (and then some) can be converted to the later by way of the "seq" function
14:42jdeisenbergYes. I can imagine that as a source of confusion to beginners.
14:42bbloomit is, hence that page :-)
14:42jdeisenbergExcept the page doesn't state the difference as succinctly and directly as you did :)
14:43bbloomjdeisenberg: i believe https://github.com/timmc/seqs-and-colls accepts pull requests, have at it :-P
14:55TimMcconfirmed
14:58tuftwhere do i get clojure.contrib.repl-utils from?
14:59jdeisenbergOK thanks.
15:00PinkPrincessHello, people. Can anyone help me with what I suspect is a fairly simple macro problem?
15:00bbloom~anyone
15:00clojurebotJust 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:00PinkPrincessRight.
15:01PinkPrincesshttps://www.refheap.com/85097 almost works, but I need line 7 to refer to the network# defined on line 3.
15:01PinkPrincessHow do I achieve that?
15:01bbloomjust drop the `
15:01bbloomthe inner one that is
15:01bbloomoh wait
15:01bbloomnevermind, you have that inside a ~
15:01bbloomyou need to use an explicit gensym
15:01PinkPrincessAh.
15:02bbloom,(let [sym (gensym)] `~sym)
15:02clojurebotG__27
15:03bbloomPinkPrincess: but you can do it w/o that here
15:03bbloomassuming command is some kind of seq, you can just do something like:
15:03PinkPrincessWithout the gensym?
15:04PinkPrincesscommand is going to be (f & args) for some commands and any number of args.
15:04bbloom,(let [cmd '(f x)] `(~@cmd x#))
15:04clojurebot(f x x__50__auto__)
15:04PinkPrincessAnd I need to inject network as the first arg.
15:04bbloomPinkPrincess: use ~@ inside a list to reconstruct the form you want
15:05bbloom,(let [cmd '(f a b)] `(~(first cmd) x# ~@(next a)))
15:05clojurebot#<CompilerException java.lang.RuntimeException: Unable to resolve symbol: a in this context, compiling:(NO_SOURCE_PATH:0:0)>
15:05bbloom,(let [cmd '(f a b)] `(~(first cmd) x# ~@(next cmd)))
15:05clojurebot(f x__99__auto__ a b)
15:05bbloomgot it?
15:05PinkPrincessThink so. I'll just see if I can make sense of it in my own code.
15:06tuftwhat's the best way to introspect a java object in the repl?
15:06bbloomPinkPrincess: 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:07justin_smithtuft I think that now you can just use clojure.repl
15:07PinkPrincessYeah, makes sense.
15:07justin_smithtuft and that comes with clojure.core
15:07bbloomjustin_smith: i think he means like pretty print or look at private fields or whatever
15:08justin_smithPinkPrincess: please share a paste of the code
15:08bbloomtuft: 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:08tuftbbloom: yeah, that's basically what i'm doing =)
15:08tuftjustin_smith: hmm, i'm not seeing the "show" function in that namespace
15:08justin_smithyeah, that ` is out of place
15:09tuftclojure.reflect seems to have some stuff though
15:09bbloomtuft: try also ##(doc bean)
15:09lazybot⇒ "([x]); Takes a Java object and returns a read-only implementation of the map abstraction based upon its JavaBean properties."
15:09PinkPrincessJust updated it, justin_smith: https://www.refheap.com/85097
15:09bbloom,(bean {})
15:09clojurebot{:empty true, :class clojure.lang.PersistentArrayMap}
15:09bbloomif you're lucky enough to have a class with getFoo and isBar functions all over it
15:09PinkPrincessSeems to work now. Thanks a lot, bbloom.
15:10bbloomPinkPrincess: cool
15:10PinkPrincessAt least lein expectations performs as before. Just with 60% less code due to the macro.
15:10justin_smithperhaps a helper function that arranges the code-as data to the form the function should return
15:10justin_smith*code-as-data
15:10justin_smith*the macro should return (no coffee, shutting up now)
15:10cbpanyone knows how to delete a project in clojars
15:11tuftbbloom: ah yeah, bean is handy
15:12tuftbbloom: wish it automatically wrapped return values, though
15:12bbloomtuft: you mean like recursively bean-ify? :-)
15:13bbloomif 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:13tuftbbloom: yeah exactly -- lazily i suppose
15:13bbloomyeah, definitely needs to be lazy
15:15ToxicFrogException in thread "main" clojure.lang.ArityException: Wrong number of args (-2) passed to: PersistentVector, compiling:(spellcast/game.clj:148:26)
15:15justin_smithtuft there is clojure.reflect, but there are also some slightly easier libs that wrap it
15:15ToxicFrogEven with macros, how the hell did I manage to pass a negative number of arguments to something?
15:16eg0u can have records inside records thats insane
15:16bbloomToxicFrog: i think that's a side effect of macros getting 2 secret args
15:16justin_smithtuft: also, sometimes I get the info I want from (class foo) + javadoc
15:17justin_smithtuft: or (bean foo) sometimes has useful output
15:17bbloomToxicFrog: and a bad error message going wrong in some case
15:18justin_smithbbloom: well, it won't be pretty but clojure.reflect will let you see private fields
15:18justin_smithdid I meantion anything called "show"? my scrollback is not finding it
15:20Glenjaminhi 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:22Glenjaminthe folding one works (but is slow), the folding transient just bails out with the ClassCastException
15:26justin_smithif you reify the beans, be sure to make a mexican food based project to put it in
15:26justin_smithlol
15:27technomancyhttp://p.hagelb.org/eyebrow.gif
15:35bbloomjustin_smith: dammit, now i'm hungry
15:37bbloomit really annoys me that protocol vars don't evaluate to an identity value
15:37bbloom,(defprotocol P)
15:37clojurebotP
15:38bbloom,(let [x P] (extend-protocol P Object) (= x P))
15:38clojurebotfalse
15:38bbloom:-(
15:39justin_smithtuft: oh you mean there is no clojure.repl/show - I just realized what you meant
15:40eg0in 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:41Bronsaeg0: (defn hello ([] (hello "ego")) ([user] (str "hello " user)))
15:42eg0so then just variable arity
15:53justin_smithI was all lagged, sorry for any lagged out non-sequitors
16:41visofhello, is there anybody have used jiraph?
16:46justin_smithvisof: ninjudd is on this channel, though he may be away
16:48visofjustin_smith: havn't you used it before?
16:48justin_smithvisof: if you have a specific question someone may be able to help, no I have not personally used it
16:49justin_smithI was pointing at ninjudd because he is the author, and he is nominally here
16:49visofjustin_smith: i can't use the basic stuff in the tutorial and yesterday i asked here but none help
16:49justin_smithvisof: what was the error
16:49visofokay
16:50visofjustin_smith: i'll paste what i did project.clj core.clj and error, give me sec
16:53visofjustin_smith: error: https://www.refheap.com/85103 , project.clj: https://www.refheap.com/85104 , core.clj: https://www.refheap.com/85105
16:56visofjustin_smith: checked?
17:01whodidthisoh no, figwheel doesnt seem to work with projects in checkouts
17:06expezAny way to get rid of the error message in lighttable?
17:07bbloomexpez: you mean like the inline evaluated ones?
17:07sverihi, 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:08expezbbloom: found it, had to reach for the mouse to click it away
17:08bbloomexpez: i think there is a "clear all" command of some sort
17:08bbloomexpez: yeah "Eval: Clear inline results"
17:09expezbbloom: thanks, but I'm really just visiting from Emacs
17:09bbloomexpez: it's cool, i'm not visiting at all from vim :-P
17:10expezwanted to go through the Om tutorial and getting clojurescript setup for emacs is a bit underdocumented
17:14benmosshm, 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:14justin_smithvisof: the error says tokyocabinet is missing, and in project.clj you have the dep on tokyocabinet commented out
17:14benmossi dont really know what would be causing that. the “put!” still returns true
17:16justin_smithvisof: also, it may require something in the sonatype snapshot repo https://github.com/ninjudd/jiraph/blob/develop/project.clj#L23
17:20benmossah
17:29visofjustin_smith: https://www.refheap.com/851079
17:30visofjustin_smith: i have changed the project.clj
17:34justin_smithvisof: that refheap is 404
17:36visofjustin_smith: https://www.refheap.com/85107
17:41justin_smiththat's weird, flatland.jiraph.core definitely exists
17:41justin_smithcan you show your updated project.clj?
17:41visofsure
17:42visofjustin_smith: https://www.refheap.com/85109 ?
17:44justin_smithvisof: that project.clj does not contain the jiraph dep
17:44justin_smithit 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:44visofso what should be the final ?
17:45justin_smithso instead of copying their whole project.clj, keep yours, and add the :repositories entry they have
17:46justin_smithhttps://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:50visofjustin_smith: isnt' it dependencies?
17:50visofjustin_smith: https://www.refheap.com/85109 there is a dependencies here
17:50justin_smithyes, but you don't actually have a dependency of jiraph
17:50justin_smithso it won't find jiraph
17:50justin_smithwhich is the error you got
17:51justin_smiththat is jiraph's project.clj - it is the dependency list that project uses
17:52justin_smithit 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:54visofjustin_smith: [org.flatland/jiraph "0.12.3-alpha1"]
17:57visofjustin_smith: https://www.refheap.com/85110 that's another error after adding line of jiraph to project.clj
17:58justin_smithok, I think one of the jiraph people (amalloy or ninjudd maybe) will have to help you with that
17:59justin_smithvisof: is this your first clojure project?
17:59amalloyi 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:00visofjustin_smith: i'm very new at clojure
18:00amalloyas for jiraph, like...we never really got around to making it user-friendly
18:00amalloyif you're not capable of contributing to jiraph, you're probably not capable of using it
18:00amalloyi would recommend something else for a first project
18:01visofso i can't use it for simple project?
18:01visofamalloy: can you help me please to setup a simple projecT?
18:03amalloyno, i can't. i really suggest you try something else
18:03visofamalloy: well, can you direct to me to start using jiraph?
18:04visofi want to use it very much
18:04amalloyconsidering all the pain it's put you through already, i can't imagine why
18:16felherHey 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:17felherMaybe I am just blind, but nothing in clojure.set seems to do the job. :)
18:17tickingfelher: update-in is used for associative things like vectors or maps
18:17mi6x3m-altfelher: this is over-engineering
18:17AeroNotixfelher: so you want to replace a value in a set with the f . v?
18:17mi6x3m-altjust use conj
18:18AeroNotixfelher: conj won't remove the previous value
18:18felherMaybe I should provide an example:
18:18AeroNotixfelher: definitely
18:18AeroNotixsounds like you
18:19tickingfelher: (set (map #(if (pred %) (f %) %) myset))
18:19AeroNotixyou've chosen the wrong datatructure to me
18:20tickingfelher: 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:21felherSay 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:21mi6x3m-altfelher: ah :)
18:21felherticking: 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:22justin_smithfelher: 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:22ticking(sorted-set (... ?
18:22AeroNotixfelher: clojure.core/map
18:22justin_smithticking: oh yeah, maybe sorted-set-by :age if that is a common operation you want to optimize for
18:23mi6x3m-altfelher: do you have a copy of "Programming Clojure" around?
18:23mi6x3m-altit provides a good overview of releational functions on sets
18:23tickingfelher: sets are not relational databases even though core.set provides a lot of relational functions
18:24mi6x3m-altselect/project/join etc.
18:24felheryeah, the problem with just putting sorted-set before it, is that I don't know if it is created with sorted-set-by.
18:24tickingfelher: what are you building? why is it relevant to return the same kind of set?
18:24felhermi6x3m-alt: yes, I do @ programming clojure)
18:25mi6x3m-altfelher: look towards the end of chapter 3
18:25mi6x3m-alt"Calling Structure-Specific Functions"
18:25mi6x3m-altyour problem is one of relational sets
18:25mi6x3m-altwhich is exactly what the chapter covers
18:25tickingmi6x3m-alt: core.set does not cover his problem
18:25seangrov`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:25mi6x3m-altyes, the updating step will be ugly
18:25tickingmi6x3m-alt: I'd be suprised if the book covered it then
18:26felherticking: 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:26mi6x3m-altticking: it doesn't cover an update for sure :)
18:26felhermi6x3m-alt: okay, I will take a look at that. :)
18:26tickingy
18:27justin_smith felher: why a set rather than a vector or seq?
18:27ticking(inc justin_smith)
18:27lazybot⇒ 39
18:29felherjustin_smith: actually, I probably would use a vector now. I realized that I needed a order quite late, when I already used sets. :)
18:30felherBut 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:32tickingist 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:32tickingwithout using cljx ^^
18:35felherWell 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:40coventrycljsbuild is not required, but it is super handy.
18:42tickingcoventry: is it still possible to have different source-paths for clj and cljs then though?
18:47jack_rabbitis anyone familiar with the ring session middleware?
18:48Frozenlock~ask
18:48jack_rabbitI 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:48clojurebotThe Ask To Ask protocol wastes more bandwidth than any version of the Ask protocol, so just ask your question.
18:48jack_rabbitFrozenlock, indeed, sorry.
18:48jack_rabbitan empty session appears. Only in the actual route handler does the populated session map appear.
18:49jack_rabbitI'm using compojure, by the way.
18:49FrozenlockIs it really 'before'? (you tried both before and after?)
18:50bbloomseangrov`: cool
18:50jack_rabbitYeah, as best as I can tell.
18:50jack_rabbitWhere I expect it to be 'before', an empty :session {} key-value pair appears.
18:50jack_rabbitWhere I expect it to be after, the key-value pair is not present in the request.
18:50jack_rabbitI've used macroexpand-1 to ensure I'm not crazy.
18:51justin_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:51clojurebot#{{:b 3, :a -1} {:b 5, :a 2} {:c 3, :b 2, :a 10} {:c 8, :a 66}}
18:51seangrov`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:52justin_smithjack_rabbit: where do you put a value in the session?
18:52bbloomseangrov`: how slow could it possibly be? :-P
18:53justin_smithjack_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:53jack_rabbitjustin_smith, in the route handler. When I check the session in another route handler, the map is populated properly.
18:53jack_rabbitjustin_smith, yes, I've done that.
18:53justin_smithOK
18:54justin_smithon 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:55justin_smithalso, 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:55justin_smithhandler middleware chaining that is
18:57jack_rabbithmm... 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:59justin_smithyeah, seems like you would want it to be the first middleware in the chain after wrap-session
19:00justin_smithdo you use the -> chain operator for describing your middleware? (-> handler middleware-1 middleware-2 ...)
19:00justin_smithwith one per line that makes it easy to shuffle the order as needed
19:00coventryticking: 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:01tickingcoventry: ok thanks ^^
19:07jack_rabbitjustin_smith, yeah. I have (-> app-routes my-middleware ring-session ring-cookies)
19:15justin_smithjack_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:15justin_smithor "normal" or whatever
19:16justin_smithI think that order is right though
19:16jack_rabbitSure, but if they did their work after, the request handler wouldn't be able to see the results, no?
19:16justin_smithright, it is the difference between pre-processing and post-processing
19:16jack_rabbitFor some it wouldn't matter. For sessions, I think it needs to be (handler (f request))
19:16justin_smithright
19:17justin_smithgood point
19:17jack_rabbithmm. I'm reading wrap-session source.
19:18jack_rabbitIt looks like it should be working correctly as I have it.
19:22jack_rabbitOH!
19:22jack_rabbitcrap.
19:22Glenjamintypo on the key?
19:23jack_rabbitDo I need to return the session from all of my handlers now?
19:23yedidoes 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:24jack_rabbitwait, no. That wouldn't explain it.
19:24justin_smithyedi: yeah, that way you can redefine the var and the new handler is being used
19:24justin_smithyedi: otherwise you need to restart the httpkit server to change the handler
19:24justin_smith(or do some other form of indirection)
19:24justin_smithit is a dev time thing
19:25yediso if you redef the handler, httpkit will start serving based on the new handler? and without the var that wouldn't happen?
19:25Glenjaminhttp://http-kit.org/server.html#stop-server
19:25justin_smithyedi: yeah, if you just used the function name and not the var, it would dereference too early basically
19:25justin_smithso it would not see updates
19:26justin_smithby passing the var, you force it to deref on each request
19:27justin_smithjack_rabbit: yes, if you retern an empty session from any request, that client now has an empty session
19:27justin_smithjack_rabbit: I used to run into this bug often :)
19:27justin_smiththe 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:28justin_smithif all handlers are returning the request that came in, then anything that needs to persist, will, as the codebase evolves
19:28jack_rabbitjustin_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:28justin_smithjack_rabbit: oh, very weird
19:28justin_smithsounds like it's time to do some tracing
19:29jack_rabbityeah... unfortunately.
19:29justin_smith(def debug (atom {})) (swap! debug assoc (java.util.Date.) request) - just put that in a bunch of places in the code
19:29justin_smiththen you see the traces at your leisure, in the repl
19:30justin_smith*can see
19:30Glenjaminlooking at the source, omitting the :session key shouldn't update or delete anything
19:30justin_smithreally? I could have sworn this has happened to me in the past
19:30justin_smithmaybe the newer version is smarter
19:30justin_smithanyway, bbl
19:53jasonjcknif 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:53jasonjckndoes anyone know a solution to this problem, e.g. a lazy seq bounded on materialization
19:53clojurebotExcuse me?
19:54bbloomjasonjckn: 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:57jasonjcknbbloom: so the solution is bounded queue/channels
19:58jasonjcknbbloom: so that the slowest consumer causes back pressure on the producer
19:58bbloomjasonjckn: precisely
19:58jasonjcknbbloom: that means all consumers will eventually consume at a rate of the slowest consumer
19:59jasonjcknthat 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:59bbloomjasonjckn: 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:59jasonjcknthe lazy seq would then need to track cursors of each consumer, and ensure the spread of cursors doesn't get too wide
19:59bbloomif neither of those things apply to you, you need to handle dropped messages
20:00bbloomwhat property of lazy seqs do you need to preserve?
20:01jasonjcknwell 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:01bbloomlol
20:01bbloom"somebody else's problem! *tosses over shoulder*"
20:01bbloomnice.
20:02bbloomif 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:02jasonjcknyes absolutely
20:02jasonjckni'll encorporate that
20:02bbloomif 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:03bbloom*shrug* oh well, you'll see that in your metric and optimize
20:03bbloomthat'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:03jasonjcknbbloom: what's your github
20:03bbloomjasonjckn: github.com/brandonbloom/
20:18nDuffI 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:22nDuffPersistentArrayMap 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:23gtrakoiy, any knowledgeable tools.nrepl folks here? I think the middleware dependency mechanism is borked.
20:23gtrakor I'm pushing its limits somehow.
20:34bstro9000in 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:35bbloombstro9000: it's immutable, that's kinda the point
20:35bstro9000Cool.
20:36bstro9000Is 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:37bstro9000such as in a hash map. could i navigate to a specific key/value pair.
20:39bbloombstro9000: 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:39bbloombeyond that, it's generally preferable to use explicit paths with get-in, update-in, etc
20:39bbloomrather than zippers, i mean
20:41bstro9000bbloom: 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:41bbloombstro9000: 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:42bstro9000Either seem reasonable to me.
20:42bbloomare you familiar with update-in ?
20:43bstro9000Not really. To be truthful, this is the first time I've ever used clojure's data structures. Coming from JS-land.
20:44bbloom,(update-in {:some {:deep {:vector [:in {:a "map"}]}}} [:some :deep :vector 1 :a] #(.toUpperCase %))
20:44clojurebot{:some {:deep {:vector [:in {:a "MAP"}]}}}
20:45bbloomlots of examples of update-in around the web, check em out
20:45bstro9000Cool thanks man
20:45bbloom,(update-in {:awesome-functions #{}} [:awesome-functions] conj :update-in
20:45clojurebot#<RuntimeException java.lang.RuntimeException: EOF while reading>
20:45bbloom,(update-in {:awesome-functions #{}} [:awesome-functions] conj :update-in)
20:45clojurebot{:awesome-functions #{:update-in}}
20:46bbloomavoid zippers for now
20:46bstro9000bbloom: any easy-to-explain reason for that?
20:46bstro9000for avoiding zippers, that is.
20:47bbloombstro9000: 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:48bstro9000bbloom: Got it. Thanks a bunch for yoru help.
20:48bstro9000*you
20:48bstro9000*your.
21:32ToxicFrogIs there a convenient way to (map) over the values of a map?
21:33bbloomToxicFrog: do you want a map out at the end?
21:34bbloomToxicFrog: if you just want to map over the values, you can easily (map f (vals m))
21:35bbloomif you want a map w new keys, you need to do something like (into {} (map (fn [[k v]] [k (f v)]) m))
21:35ToxicFrogbbloom: I want a map with the same keys and different values
21:36ToxicFrogThat is basically what I'm already doing, but I was wondering if it already existed and I just don't know the name
21:36bbloomsadly not
21:36ToxicFrogBecause I've already rolled my own versions of like three other library functions in this project and then later discovered they already existed
21:51krasToxicFrog: typical clojure beginning :-)
21:53krasAre there any alternative clojure implementations specifically targetting compiling to machine code? Something like cython for python
21:55gtrakkras: closest thing is probably clojure-scheme->gambit-scheme->C->machine-code.
21:55gtrakhttps://github.com/takeoutweight/clojure-scheme
22:00krasgtrak: thanks, will take a look
22:03ivankras: are you solving the startup time problem or the memory usage problem?
22:04ivanif it's just startup time I was going to suggest using Linux + CRIU to create and restore Clojure zygote processes
22:04krasivan: its more to do with c/c++ interop
22:05ivanah
22:08krassomething like CFFI for CL http://common-lisp.net/project/cffi/manual/cffi-manual.html#Introduction
22:14bbloomis 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:14bbloomor use an extra atom or something to write out to
22:16bbloomnevermind, seems like swap just does an infinite loop. i'll do that w/ a compare-and-set!
22:17ddellacostabbloom: 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:18bbloomddellacosta: 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:19ddellacostabbloom: gotcha.
22:21ToxicFrogOh what the hell
22:22ToxicFrogThere's already a 'mapv' function for map over vectors?!
22:24Cr8mapv is map that -returns- a vector
22:25Cr8and, therefore, isn't lazy
22:25Cr8its input coll can still be whatever
23:03ToxicFrogPSA 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:03ToxicFrogJust close!ing it will result in irritating and hard to track down bugs as messages vanish.
23:03ToxicFrogOr possibly get stuck, I'm not sure!
23:04ToxicFrogI would have expected it to automatically unsub based on the description of (tap), but apparently not.