#clojure log - May 04 2014

The Joy of Clojure
Main Clojure site
Google Group
List of all logged dates

0:35 eg0: anybody familiar with light table? cant save anything, get eacces permission denied

0:36 even in user directories

0:37 does it need to run as root?

1:03 TEttinger: eg0: that's awfully odd

1:03 on windows?

1:04 eg0: TEttinger: ubuntu

1:04 reinstalling everything now

1:16 itimmy: Allow me to introduce myself, my name is itimmy and I was sent here from the gods

1:26 satshabad: I want something like an some-> except that in adition to a nil, I get an error message back too

1:26 addition*

1:26 I... I think I want a Monad?

1:27 Can I use maybe ..half a monad or something?

1:27 amalloy: satshabad: yeah, you kinda want Either. but hey, exceptions have been the special-case monad for that in java for years

1:28 satshabad: try catch? hmm, I was kind of hoping for elegant tonight

1:29 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

1:30 this is interesting: http://adambard.com/blog/acceptable-error-handling-in-clojure/

1:40 amalloy: satshabad: that's literally just a hand-rolled implementation of Either

1:40 satshabad: haha I know :)

1:40 amalloy: which, okay, Either is pretty cool. but if you wanted to avoid monads...

1: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

1:42 eg0: TEttinger: got things going

1:42 TEttinger: that's good

1:42 what do you think caused it, eg0?

1: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?

2:06 TEttinger: Morgawr, that would be nice wouldn't it... I don't think it exists right now

2:06 Morgawr: TEttinger: a shame :(

2:07 TEttinger: there might be a read-through of core.clj somewhere

2:07 Morgawr: I would love for somebody to create a Clojure dialect that runs on rust

2:07 TEttinger: but really what you're interested in is the Java side, or JS side?

2:07 or LLVM!

2:07 Morgawr: rust is on llvm

2:07 so yeah

2:08 I'm interested in the process of building a clojure language that runs on <whatever platform>

2:08 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)

2:09 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

2:09 TEttinger: people seem to be implementing kernel a lot

2:09 http://axisofeval.blogspot.com/2011/09/kernel-underground.html

2:10 lisp-like language

2:10 Morgawr: TEttinger: nice

2:11 TEttinger: might be a good place to start, Wat was 400 lines of JS at one point IIRC

2:11 (and could eval at that point)

2:12 Morgawr: https://github.com/brandonbloom/eclj this seems interesting

2:14 TEttinger: woah

2:15 bbloom, that's slick

2:15 Morgawr: oh, it's from bbloom

2:15 neat

2:17 http://www.infoq.com/presentations/Clojure-LLVM/ this is also probably worth watching

2:18 eg0: oooo thats awesome

3: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?

3:49 oh ns-aliases

3:52 quizdr: any clojurescript users in here at the moment? I have a question on compiling into the js file

4:00 Morgawr: quizdr: just ask your question and people might answer :)

4: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?

4: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

4:09 and if you don't have optimiations (google closure's pruning, etc etc) it's going to be quite lengthy of course

4:09 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)

4:10 there is a clojurescript compiler if I remember correctly but everything is abstracted away thanks to lein so I don't know more

4: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.

4: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

4:11 TEttinger: quizdr, it is in java too. that's what the dependency on clojure.jar is

4: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)

4:11 TEttinger: (clojure.jar has all of the stuff in core.clj and related ns's)

4:12 * Morgawr is afraid of claiming bullshits

4:12 TEttinger: it might be good to investigate what's in the jar that lein downloads, Morgawr

4: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

4:13 Morgawr: TEttinger: definitely

4:13 I mean, I know about clojure.jar, I just wasn't sure about javascript

4:14 quizdr: http://stackoverflow.com/questions/19963948/how-to-set-the-clojurescript-print-fn-fn-in-nodejs this might be useful

4:14 I usually use (.log js/console ...) in clojurescript

4:14 ptcek: Is there any way to define records with defaults (other than macros like http://cemerick.com/2010/08/02/defrecord-slot-defaults/)

4: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

4:16 pprint, zip, xml

4:16 guns: ptcek: I think a custom ->Record constructor is the best answer. A little verbose but explicit

4:17 Morgawr: I usually write a myrecord-init function for all my defrecords

4:17 that macro linked might turn out useful (but I dislike the name)

4:18 guns: It's sometimes useful to create records with implicit fields (like timestamps). that's just more macro magic of course

4: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?

4:19 and I mean, I got a critical boost in performance from such a change in my code

4:19 guns: what kind of % delta?

4:19 Morgawr: original code ran in 12seconds, with the record re-instantiation it ran in 6-7 seconds

4:20 so, cut down the run time in half

4:20 guns: pretty big. I guess the ctor doesn't even bother with the IPersistentMap interface

4:20 just a guess

4: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)

4:21 guns: oh yeah, that should be faster

4:21 Morgawr: good to know :)

4:21 guns: assoc with multiple values just does a simple reduce

4:21 Morgawr: ah

4:21 so it's building several layers?

4:21 * Morgawr wonders if using transients in that instance makes it go faster

4:21 guns: it does the persistent coll thing

4: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

4: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

4:23 I realize the vectors might not be best suited for this operation, open to suggestions

4: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)

4:24 afhammad: why not use a map of maps and store the id as key?

4:25 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)

4:26 guns: And if all the :id values are positive ints, you can use the new immutable-int-map library for performance

4:26 Morgawr: guns: oh, never heard of that, got any resources? it might turn out to be useful for one of my projects

4:27 guns: Morgawr: https://github.com/ztellman/immutable-int-map

4:27 It screams "build a trie"

4:28 Morgawr: aw, it uses parallel threads.. cool but it's not going to work for me

4:28 afhammad: FYI this is in ClojureScript

4:28 Morgawr: still an interesting library

4:28 afhammad: shouldn't matter

4:29 guns: Morgawr: It only mentions parallel threads in the discussion about the reducers library

4:29 it is totally concurrency agnostic

4:29 Morgawr: ah

4:29 so it doesn't use threads in the implementation, good good

4:29 I misunderstood the readme then

4:29 afhammad: also I need to traverse the collection sequentially as it is being rendered as dom elements

4:30 guns: it just so happens to be very good at merging

4:30 Morgawr: afhammad: you can get a list out of a map using the vals function

4:30 not sure if it's faster/slower though

4:31 afhammad: ok, will look into it. Thanks guys

4:36 Morgawr: https://code.google.com/p/jvmtop/ just found out about this, pretty cool tool

4:36 thought it'd share

4:38 guns: Morgawr: oh that is useful! It's nicer than htop showing a 100 "java" threads

4:38 Morgawr: guns: yeah, exactly

5: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?

5: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

5:30 https://www.refheap.com/85084

5:31 I wonder if an easier way might be to write a function that converts this whole thing to a hash map (omitting attributes)

5:32 (just noticing that this wouldn't work with <b><a>hello</a><a>world</a></b>)

5:36 TEttinger: martinklepsch, I can see why you'd want to parse that patent in particular, that's hilarious

5:36 martinklepsch: TEttinger, novelty jeans for the win!

5:37 TEttinger: so you need to parse malformed HTML?

5:37 err XML

5:38 I guess that example wasn't malformed

5:39 so what kind of data are you trying to get out of it, martinklepsch?

5: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)

5:40 Morgawr: I've been reading Clojure High Performance Programming and wow it is a good book

5:41 I strongly recommend it

5:41 martinklepsch: TEttinger, right now I want to build a UID by the stuff thats in the document-id tag

5:41 TEttinger: I uh... wouldn't expect to hear that that book exists

5: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

5:42 really a good read

5:42 and it's also relatively short too, only 150ish pages

5:42 TEttinger: Morgawr, will you do the hard work and optimize Cloister for users like maybe me?

5:43 Morgawr: TEttinger: yeah, this is part of why I'm reading this book :)

5:43 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

5:44 and with the knowledge I gain I hope I can work more on Cloister during the summer

5:44 TEttinger: martinklepsch: honestly the simplest possible way I can think of is to hash the contents of that document-id

5:44 (might want to not use a JVM hash function, use your own)

5:50 neat, clojure MD5 that shouldn't change on JVM updates. http://zackmdavis.net/blog/2014/01/consistent-hashing/

5:51 xecycle: Does the repl completion support Java-only classes?

6: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

6:00 TEttinger: indeed...

6:02 penthief: cider-jack-in (v0.6.0alpha) doesn't seem to use :repl-options like "lein repl" does. Am I missing something?

6: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

6:30 TEttinger, ^ somewhat related to my previous question, if you're curious

6:30 TEttinger: I actually have no idea how to use the clojure zippers, sorry

6:34 martinklepsch: TEttinger, has nothing to do with clojure zippers actually

6: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

6:38 any idea what's wrong?

6:39 martinklepsch: Kneiva, seems like a dependency issue?

6:39 http://www.slf4j.org/codes.html#StaticLoggerBinder

6: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?

6:39 Kneiva: that has been there before

6:40 Morgawr: as in, are agent creations also buffered inside a transaction?

6:40 Kneiva: martinklepsch: that ns_tracker stackoverflow is the new thing

6:43 AeroNotix: Morgawr: try it?

6: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?

6:46 AeroNotix: Morgawr: use a ref with two transactions, one transaction completes quickly (e.g. 50ms) and the other slow (e.g. 500ms)

6:46 Morgawr: I tried this

6:46 (dorun (pmap (fn [_] (dosync (alter r inc) (let [b (agent 0)] (send b (fn [_] (swap! x inc)))))) (range 1 10000)))

6:46 where r is a ref and x is an atom

6:46 and if @r == @x then it works

6:46 and it does workb

6:47 but not sure about retries and contention

6:47 (cause there's none)

6:47 is there any way to force retrying a transaction?

6:48 AeroNotix: Make two transactions

6:48 (do (future (dosync ...)) (future (dosync)))

6: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))))

6:54 this doesn't seem to be working

6:55 x is 0 :|

6:55 (f and r are refs, x is an atom)

6:56 AeroNotix: what do you mean "not working" ?

6:56 Morgawr: also it doesn't make sense, f and r should both be 0 but they become -99 and 99

6:56 AeroNotix: I mean I'm getting weird results

6:56 AeroNotix: Morgawr: explain

6:57 dissipate: what happens if you use memoize on a non-referentially transparent function?

6: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)

6:58 and on top of that the atom is never modified

7:00 AeroNotix: dissipate: sadness happens

7:00 Morgawr: show a full example

7: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)

7:01 Morgawr: amalloy: oh.. derp I used x as name for the doseq counter.... that's why

7:01 and I fucked up the syntax

7:01 welp

7:03 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

7:03 unless I messed up something else too

7: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?

7:03 Kneiva: hmm, changing back to 1.5.1 does not remove the problem

7:06 AeroNotix: dissipate: try it !

7:06 dissipate: amalloy, is it common for programmers to abuse atoms? :(

7:06 AeroNotix: Do people forget they have a REPL

7: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

7: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.

7: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

7:10 and if on a subsequient function call that key/val pair is found then it returns the result alread in the map

7:10 else it runs the function

7:10 so it makes sense that the function is only run once (with its side effects or whatever)

7: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?

7:12 Morgawr: dissipate: https://en.wikipedia.org/wiki/Memoization this?

7: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."

7:15 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.

7:15 Morgawr: how can you check if a function is non-referentially transparent? it means it ought to have no side effects

7:15 and then we enter into haskell's domain

7:16 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

7:17 AeroNotix: dissipate: it's still a non-referentially transparent function, just because you cache the results doesn't give it a separate term

7:18 dissipate: Morgawr, hmm, that's an interesting question. is there a test you can apply to detect if a function is not pure?

7:19 AeroNotix, sounds correct. and so it is probably considered bad practice to memoize a non-pure function?

7: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

7: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

7:25 AeroNotix: dissipate: you can

7:25 wrap code in (io!)

7:25 this is used in dosync blocks

7:26 Clojure 1.6.0

7:26 user=> (defn foo [] (io! (println "foo")))

7:26 #'user/foo

7:26 user=> (dosync (foo))

7:26 IllegalStateException I/O in transaction user/foo (NO_SOURCE_FILE:1)

7:26 dissipate: Perhaps memoize could use similar semantics

7:27 Morgawr: this is only for I/O though, not for all types of impurity

7:27 AeroNotix: Morgawr: what other types are there?

7:27 Morgawr: anything that has side effects

7:27 like swapping on an atom for example

7: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.

7:27 AeroNotix: Morgawr: sure

7:28 dissipate: AeroNotix, but the programmer has to use io!

7:28 AeroNotix: dissipate: sure

7:28 martinklepsch: Kneiva, that was indeed hard to see :D

7: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

7:29 AeroNotix: sigh

7:29 Morgawr: and the other would be to run the code and detect if it tries to access some "unsafe" memory areas

7:29 tainted areas or whatever

7:29 AeroNotix: double sigh

7:29 Morgawr: but in any way, that's outside the scope of clojure and it's a big research problem

7:31 4

7:32 whops, sorry

7:32 dissipate: Morgawr, i thought the problem had been solved. in Haskell everything is pure, with side effects relegated to monads, no?

7:32 Morgawr: dissipate: that's at compile time though

7:32 not runtime

7:34 dissipate: Morgawr, so you are saying it's possible to have side effects outside of monads in Haskell at runtime?

7:34 AeroNotix: lol

7:35 Morgawr: your whole pc is a box full of side effects :P

7:35 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

7:35 (although haskell does have unchecked exceptions -I think- and unsafe blocks)

7:41 dissipate: Morgawr, as i suspected, memoize is using swap!. that being the case, i'm wondering why it's not memoize! instead

7:42 Morgawr: for the same reason why alter is not alter! or send is not send!

7:42 also memoize is using an atom internally, the developer doesn't really need to know

7: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 !

7:44 TEttinger: I think the ! is more for destructive in-place modification

7:44 probably wrong

7: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

7:45 I'd use ! in every reftype operation just to have consistency but apparently this is not the case, so idk

7: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!

7:46 Morgawr: no, my argument is that it's memoize because somebody decided so, I don't know about the actual reasoning

7:47 and yes, since the atom is just internal implementation it doesn't really matter because it's never exposed

7:47 TEttinger: ah!

7:47 Use the bang! only for things not safe in an STM transaction.

7:47 http://dev.clojure.org/display/community/Library+Coding+Standards

7:48 Morgawr: TEttinger: makes sense

7: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.

7:48 TEttinger, but that raises the question. is it safe to memoize a function within a transaction?

7:49 or rather to call a memoized function within a transaction

7:49 Morgawr: dissipate: that's a good question, considering the behavior (the memoized function should be referentially transparent), I'd say yes

7:49 TEttinger: all I gotta say is question mark

7:49 Morgawr: even in the worst case, a transaction is retried and the function is called again

7:49 but instead it already has the cached value

7:49 TEttinger: http://www.badlogicgames.com/wordpress/?p=3428 time to make a lein plugin for this?

7:50 Morgawr: for play-clj?

7:51 TEttinger: no, for in general

7:51 dissipate: Morgawr, hmm. and the transaction isn't going to detect the swap! occurring in the memoized function and then throw an exception?

7:51 Morgawr: TEttinger: ah, I only read the title...but I see now, cool stuff

7:52 dissipate: no, you can use swap! and other side-effecting functions inside a transaction, you shouldn't but you can

7:52 if you do, just make sure they are idempotent and don't create problem when retried, which is the case of memoized functions

7:52 s/of/for/

7:53 dissipate: hmm, i'm not sure if that's good. it means the judgement is left to the programmer

7:54 Morgawr: it's definitely not a good thing to do

7:54 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

7:55 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

7:56 I do it to query requests to the audio engine from any thread without fear of replaying the request

7:56 (like play/pause/stop audio)

7:56 https://github.com/Morgawr/Cloister-Engine/blob/master/src/cloister/sound.clj#L191 <- this

7:56 it's not the best but it seems to work

7:56 AeroNotix: I'm sure I've seen something that is guaranteed to do things once

7:57 I'm afk at the moment

7:57 ,(once-only)

7:57 clojurebot: #<CompilerException java.lang.RuntimeException: Unable to resolve symbol: once-only in this context, compiling:(NO_SOURCE_PATH:0:0)>

7:57 Morgawr: that's for macros

7:57 AeroNotix: ,once-only

7:57 clojurebot: #<CompilerException java.lang.RuntimeException: Unable to resolve symbol: once-only in this context, compiling:(NO_SOURCE_PATH:0:0)>

7:57 AeroNotix: ah yes

7:58 dissipate: Morgawr, i see, interesting

8:02 Morgawr, by using the agent in the transaction there is no possibility for a retry?

8:02 amatsu: Is there an official #clojure pastebin?

8:02 Morgawr: amatsu: refheap.com but not sure how "official" it is

8:03 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

8:03 so in case of retries they aren't sent

8:04 amatsu: OK. So I was wondering whether this is particularly idiomatic Clojure https://www.refheap.com/85088

8:04 dissipate: Morgawr, and why is that considered to be a 'hack'? sounds good to me

8: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

8:06 TEttinger: that looks perfectly reasonable, amatsu

8:07 amatsu: TEttinger: alright, thanks!

8:07 dissipate: Morgawr, are you trying to make Cloister-Engine *the* game engine for clojure?

8:07 TEttinger: I should ask, amatsu, did the transient code work the first time? I have had such bad luck with transients!

8:07 Morgawr: nah, I'm trying to make cloister-engine a usable game engine

8:08 play-clj is probably a better option

8:08 dissipate: Morgawr, what kinds of games is it going to support?

8: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

8:08 2D games

8:08 it's still far from being done

8:08 currently working on the physics engine which is tricky because it's a non-threadsafe java engine and I'm wrapping around it

8:09 dissipate: wow, the physics namespace is pretty short

8: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)

8:09 TEttinger: once I figured that out it worked perfectly.

8:09 Morgawr: dissipate: well, it's incomplete :P

8:10 I mean, it runs physics thread but there's still no function to set physical bodies yet lol

8:10 dissipate: Morgawr, i see. i'm doing a game thing myself. but it's games for programmed bots, not human players.

8:10 Morgawr: dissipate: ah, cool

8:13 TEttinger: dissipate, what's that used for, AI research?

8: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

8:14 Morgawr: dissipate: good luck then :) Clojure is a great language

8:14 dissipate: TEttinger, yep, pretty much. but with a twist.

8:17 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.

8:21 TEttinger, more info here https://github.com/dissipate/matching-pennies-test

9: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

9:50 TEttinger: I think that's a special property of into, but I'm not sure

9:51 akazlou: also (conj {} [1 2]), but this one is mentioned here http://clojure.org/data_structures on the Maps section

9:51 TEttinger: err yes I messed up

9:51 into uses conj internally

9:52 akazlou: o, nice

9:52 TEttinger: into is equivalent to (fn [to from] (reduce conj to from))

9:52 for non-editable collections, anyway

9:52 for editable ones it uses transients

9: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 :)

9:56 thanks TEttinger

9:57 TEttinger: heh no prob

9:57 it's usually more active here... maybe the san francisco people aren't up yet :-)

9: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 Why is this wrong? (defn add [student grade] {})

10:20 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 thus the second form after -> expands to (add {} "marry" 3)

10:23 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 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 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 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 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 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 I love the diamond wand

10:53 it should be part of clojure.core imo :P

11:03 Glenjamin: "-<><:p"

11:03 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 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 I want something that takes a function of item => new channel of item

12:37 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 exactly - my f returns a channel

12:39 I have a function that takes a stripe event id and returns a channel of a stripe event

12:39 and I have a channel of stripe IDs that come in from a webhook

12:39 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 but what I'm describing is the monadic bind,

12:41 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 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 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 3) should call that argument "in" as is the convention now

12:47 4) that's map, not flatmap, you need an inner loop to accomplish a flatmap

12:48 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 roelof: use let

12:55 roelof: actually, you have quite a few errors in there

12:55 roelof: your'e not actually calling ==

12:55 sritchie: bbloom: that actually seems to work

12:55 with your changes

12:55 bbloom: it is in fact flatmap

12:55 bind, rather

12:55 I don't want to worry about sequences inside

12:56 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 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 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 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 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 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 roelof: instarepl is your friend

13:04 roelof: it "just works"

13:05 sritchie: the issue is that CSP exists to coordinate independent processes & just happens to not be totally useless for stream processing

13:05 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 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 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 start with that

13:09 don't start with webhooks

13:09 b/c you dont strictly need that

13:09 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 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 since mandrill doesn't expose an API to check on unsubscribes that have occurred recently

13:13 bbloom: I hear you on doing batch processing first

13:15 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 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 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 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 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 (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 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 i have no idea what "make available" means

13:37 use copy paste for code fragments

13:38 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 other than it's some approximation of a smalltalk environment written in c++

13:47 ... 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 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 hence the office joke

13:51 seangrov`: Yeah, I don't think I would have got that joke ;)

13:52 * seangrov` starts thinking about COM, OLE, MFC32...

13:53 bbloom: yeah, ppl don't realize that modern windows development is very smalltalk like... thanks to COM/OLE

13:53 but smalltalk like with a shitload of C++ cruft that makes you want to cry

13:53 and looks nothing like smalltalk, sadly lol

13:53 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:54 * seangrov` dreams of being in a position to 'sell' things that way

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 (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 is this accurate?

14:37 bbloom: jdeisenberg: yes

14:37 jdeisenberg: it's worth understanding them though

14:37 i mean the differences between the two

14:37 ~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 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 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 https://www.refheap.com/85097 almost works, but I need line 7 to refer to the network# defined on line 3.

15:01 How do I achieve that?

15:01 bbloom: just drop the `

15:01 the inner one that is

15:01 oh wait

15:01 nevermind, you have that inside a ~

15:01 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 assuming command is some kind of seq, you can just do something like:

15:03 PinkPrincess: Without the gensym?

15:04 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 ,(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 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 *code-as-data

15:10 *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 bbloom: wish it automatically wrapped return values, though

15:12 bbloom: tuft: you mean like recursively bean-ify? :-)

15:13 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 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 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 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 lol

15:27 technomancy: http://p.hagelb.org/eyebrow.gif

15:35 bbloom: justin_smith: dammit, now i'm hungry

15:37 it really annoys me that protocol vars don't evaluate to an identity value

15:37 ,(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 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 justin_smith: i'll paste what i did project.clj core.clj and error, give me sec

16:53 justin_smith: error: https://www.refheap.com/85103 , project.clj: https://www.refheap.com/85104 , core.clj: https://www.refheap.com/85105

16:56 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 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 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 can you show your updated project.clj?

17:41 visof: sure

17:42 justin_smith: https://www.refheap.com/85109 ?

17:44 justin_smith: visof: that project.clj does not contain the jiraph dep

17:44 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 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 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 so it won't find jiraph

17:50 which is the error you got

17:51 that is jiraph's project.clj - it is the dependency list that project uses

17:52 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 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 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 if you're not capable of contributing to jiraph, you're probably not capable of using it

18:00 i would recommend something else for a first project

18:01 visof: so i can't use it for simple project?

18:01 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 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 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 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 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 "Calling Structure-Specific Functions"

18:25 your problem is one of relational sets

18:25 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 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 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 an empty session appears. Only in the actual route handler does the populated session map appear.

18:49 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 Where I expect it to be 'before', an empty :session {} key-value pair appears.

18:50 Where I expect it to be after, the key-value pair is not present in the request.

18:50 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 justin_smith, yes, I've done that.

18:53 justin_smith: OK

18:54 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 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 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 do you use the -> chain operator for describing your middleware? (-> handler middleware-1 middleware-2 ...)

19:00 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 or "normal" or whatever

19:16 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 good point

19:17 jack_rabbit: hmm. I'm reading wrap-session source.

19:18 It looks like it should be working correctly as I have it.

19:22 OH!

19:22 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 yedi: otherwise you need to restart the httpkit server to change the handler

19:24 (or do some other form of indirection)

19:24 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 so it would not see updates

19:26 by passing the var, you force it to deref on each request

19:27 jack_rabbit: yes, if you retern an empty session from any request, that client now has an empty session

19:27 jack_rabbit: I used to run into this bug often :)

19:27 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 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 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 then you see the traces at your leisure, in the repl

19:30 *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 maybe the newer version is smarter

19:30 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 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 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 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 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 "somebody else's problem! *tosses over shoulder*"

20:01 nice.

20:02 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 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 *shrug* oh well, you'll see that in your metric and optimize

20:03 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 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 or I'm pushing its limits somehow.

20:26 * nDuff headscratches -- this looks very much like an issue found and fixed last November.

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 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 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 beyond that, it's generally preferable to use explicit paths with get-in, update-in, etc

20:39 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 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 *you

20:48 *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 ToxicFrog: if you just want to map over the values, you can easily (map f (vals m))

21:35 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 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 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 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 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 or use an extra atom or something to write out to

22:16 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 There's already a 'mapv' function for map over vectors?!

22:24 Cr8: mapv is map that -returns- a vector

22:25 and, therefore, isn't lazy

22:25 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 Just close!ing it will result in irritating and hard to track down bugs as messages vanish.

23:03 Or possibly get stuck, I'm not sure!

23:04 I would have expected it to automatically unsub based on the description of (tap), but apparently not.

Logging service provided by n01se.net