#clojure log - Nov 10 2013

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

0:00 bitemyapp: algal: don't.

0:00 algal: either your SQL database is your point of truth or it isn't, don't try to be clever.

0:00 algal: bitemyapp: okay. what should I do instead? (honest question.)

0:00 bbloom: algal: what database are you using?

0:00 algal: postgresql

0:00 bitemyapp: algal: what sort of invariants are you talking about?

0:00 algal: just write simple code.

0:01 bbloom: ok, so an actual database :-)

0:01 algal: I've written simple code, but I'm not experienced with multithreaded programming at all so I am not confident it is correct.

0:01 bbloom: algal: here's a good place to start with databases: http://www.postgresql.org/docs/9.1/static/transaction-iso.html

0:01 bitemyapp: algal: don't try to move consistency outside of the database.

0:02 algal: your database should be resolving all conflicts and handling all transactions.

0:02 algal: in brief, don't try to be clever. it'll bite you no matter what language you use.

0:02 algal: Here's an invariant: I have a database table that has columns USERNAME and SESSIONID, where SESSIONID is a UUID.

0:03 I want to ensure that every username has 3 or less sessionIDs.

0:03 bbloom: algal: wrap your writes in transactions and be aware that multiple independent reads are not necessarily consistent with each other. if you need consistent reads with complex multi-query processes, you need to either write stored procedures, or treat your database as append-only and use good IDs and timestamps and whatnot

0:04 bitemyapp: algal: that has to happen in the database.

0:04 algal: what bbloom said precisely.

0:04 bbloom: algal: alternatively, you can do something simpler: allow them to go over 3 sessions, but afterwards, kill the oldest N-3 sessions

0:05 algal: bbloom: Thanks, this is very helpful. When you say "wrap your writes in transactions", you mean all within postgresql right?

0:05 bbloom: (not STM transactions)

0:05 bbloom: algal: right. if you issue multiple mutations from your client, you want to wrap them in a database transaction

0:06 that's not the default in rails, for example, and people get all sorts of stupid bugs b/c of it

0:06 bitemyapp: bbloom: gotta win those benchmarks yo.

0:06 bbloom: things like `validate_uniqueness_if :username` simply *DO NOT WORK* if they are based on a read and then a write with a network round trip in the middle

0:06 algal: bbloom: Okay. Thanks. This is quite helpful. So why is it a simpler alternative to kill the oldest 3 sessions? Doesn't that complicate things by requiring me to timestamp sessions and introduce a new process that kills old sessions?

0:07 bbloom: algal: you fetch somebody's session on every request right?

0:07 when you say SELECT * FROM sessions WHERE id = ?;

0:07 then you only get one

0:07 but you can do:

0:08 INNER JOIN sessions AS self ON sessions.user_id = self.user_id

0:08 that will give you all of the sessions for each user. if it's greater than 3, you can issue a query to clear it

0:08 then there is no cron process

0:08 if multiple threads all try to drop all older than 3, then the behavior is *idempotent*

0:09 there is no need for a coordinated read/write pair to prevent going over 3

0:09 if you want to reeeaaally prevent going over 3, you need a stored procedure

0:09 your very first stored procedure has a very high complexity cost, b/c now you are versioning your code INSIDE your database

0:09 if you can get away with no stored procedures, that's great, but realistically you'll need some in a large app

0:10 algal: bbloom: Wow, this is turning out to be just as complicated as I feared. I though this was an amazingly simple requirement..

0:10 But I am quite eager to understand this fully and rigorously.

0:10 Would you say the postgresql page is a good place to start?

0:10 bbloom: postgres' manuals are excellent

0:11 another approach is to implement a trigger

0:11 the problem is that you don't want "fully serialized" transaction isolation

0:12 it will kill your performance

0:12 so it's always best to come up with designs that work if they fail in the middle

0:12 algal: bbloom: So would it be correct to say here, that if I'm using postgresql, then all of Clojure's vaunted concurrency stuff is pretty much irrelevant because I'm effectively pushing all the consistency requirements into the db?

0:13 bbloom: algal: clojure's concurency primitives are for *threads*, but you're going to have *clients*

0:13 you need consistency between machines

0:13 your database can provide that

0:14 that's not to say they aren't useful for other things

0:14 algal: But in a servlet (this is a ring app), isn't the issue that separate clients could be represented by separate threads?

0:14 bbloom: right, but you can treat separate threads the same was as you'd treat separate boxes

0:15 you can think of it ass an optimization to start sharing memory & communicating between threads

0:15 for example, if you cached some stuff in memory, an atom would be a great place to store that

0:16 algal: okay.

0:16 bbloom: bitemyapp: rails doesn't win any benchmarks ever. so i'll just s/rails/mongo/

0:17 i haven't read it myself, but i hear great things about http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601

0:18 bitemyapp: bbloom: no, no no

0:18 bbloom: don't recommend JCIP

0:18 algal: bbloom: Yes, I've been meaning to read that one.

0:18 bbloom: ok, nevermind then

0:18 bitemyapp: no

0:18 bbloom: lol

0:18 bitemyapp: don't.

0:18 algal: don't.

0:18 algal: save yourself.

0:18 algal: bitemyapp: why not??

0:18 lazybot: algal: Uh, no. Why would you even ask?

0:18 xuser: isn't his underlying http server (jetty or http-kit) making his ring app concurrent already? without doing any manual concurrency in the clojure code

0:18 bbloom: my understanding was that it's a good intro to threading in general

0:18 xuser: yes

0:18 algal: basically, for the typical app, all you need to do:

0:19 bitemyapp: algal: it's a terrible, terrible book.

0:19 I'm *still* angry I bought it.

0:19 bbloom: algal: 1) be *aware* that multiple reads involve multiple network round trips, and other stuff might happen in between reads

0:20 algal: bbloom: Yes, this awareness is exactly what's got me puzzled about how to do everything correctly.

0:20 bbloom: algal: 2) when doing multiple writes, subsequent writes may fail, and so you need a transaction to make them operate as a unit

0:20 algal: when in doubt, re-read http://www.postgresql.org/docs/9.1/static/transaction-iso.html a few times

0:20 algal: "Read committed" is the default and almost 100% definitely what you want unless you really know better

0:21 algal: see http://www.postgresql.org/docs/9.1/static/server-programming.html for doing stuff "over there" on the server, if you need to for better consistency

0:22 algal: and watch Rich Hickey's "are we there yet" video for an explaination of perception :-)

0:22 that's about it

0:22 if you do that, your app will be 100X better than every rails app ever

0:22 you've made an excellent choice to use an actual database

0:22 don't use an ORM

0:23 algal: bbloom: Does Korma count as on ORM? I'm using clojure.java.jdcb 0.2.x, but mostly because I wanted to understand what was happening.

0:23 But I was wondering if Korma would be a better way to handle it.

0:23 xuser: algal: korma is just a dsl I think, sugar

0:23 bitemyapp: Korma ain't an ORM. It's a DSL for SQL.

0:23 algal: ok.

0:24 bbloom: ehh... there are some ORM-ish things in there

0:24 but it's certainly no active record

0:24 xuser: algal: and it probably uses clojure.java.jdbc under the hood

0:24 algal: You said I don't want "'fully serialized' transaction isolation". Does that just mean "fully locking the database for use by only one client during the scope of my operation"? I wonder if this might be okay for me. I have very low performance requirements. I am mainly worried about correctness.

0:25 bbloom: algal: well then, it's time for me to link to my favorite blog post of mine: http://www.brandonbloom.name/blog/2013/06/26/slurp-and-spit/

0:26 algal: thanks, will have a look right now.

0:27 swarthy: bbloom: reading this, love the title on your site

0:27 bbloom: swarthy: thanks!

0:28 bitemyapp: bbloom: yeah I like that too.

0:29 bbloom: I was shouting that at my computer one day in college working on a project & my roommate told me it would be a good name for a book, if I ever wrote one day

0:31 algal: bbloom: Anything you'd recommend besides postgreSQL docs and Goetz's book? I'm late to the part on this stuff but I'd like to understand it properly and finally, rather than swim around vaguely for years.

0:32 bbloom: algal: so i had the (un)lucky experience of maintaining an *ancient* win31 app w/ a crazy custom database at some point early in my career, so i picked up a lot of database knowledge that way... but i'd guess that if there is something worth reading about this, it's on rich's bookshelf...

0:32 clojurebot: c'est bon!

0:33 bbloom: http://www.amazon.com/Clojure-Bookshelf/lm/R3LG3ZBZS4GCTH

0:33 algal: good thought

0:34 bbloom: if i ever move someplace w/ a few extra hundred square feet, i may buy some of those books :-P

0:34 algal: a hundred spare months is the real bottleneck, I reckon.

0:35 bbloom: who said anything about reading them?

0:35 algal: :)

0:37 swarthy: bbloom: oh wow this is cool, going through your old posts I found the 'Read the Source' post. I remember reading this when it came out!

0:38 bbloom: swarthy: nice! i'm a nameless celeb!

0:38 i'm pretty sure atwood owes me royalties, but i'll let it slide :-P

0:38 swarthy: ahah

0:44 bbloom: algal: so is your app a candidate for spit & slurp? :_)

0:44 swarthy: bbloom: sorry to pry but I'm reading through a bunch of your old posts and liking them. I notice you had a startup, and LinkedIn says consulting? Using clojure for those?

0:45 bbloom: swarthy: sadly not much

0:46 algal: oh, it would be based on data volume and data complexity. But I need to use postgresql for non-functional reasons (a client just likes it).

0:46 Also, I'm deploying on EC2 and I have the impression that EC2 instance's filesystems are not to be relied on for durability.

0:46 swarthy: cool. Had you already built it with other stuff?

0:46 bbloom: well, at least your client has good taste :-P

0:46 algal: bbloom: :)

0:47 muhoo: didn't chris write something called simpledb that is basucally spit/slurp an atom as edn?

0:47 bbloom: somebody did

0:48 algal: SimpleDB is the name of one of Amazon's nosql services. or is this a different one?

0:48 bbloom: different

0:48 i think it was alandipert

0:48 allenj12: hey if i have a list of items i want to remove from another list, how would i do that?

0:49 bbloom: https://github.com/alandipert/enduro

0:49 allenj12: how large are your lists?

0:49 justin_smith: allenj12: (remove (set unwanted) all)

0:49 bbloom: yeah, what justin_smith said, for most use cases

0:49 justin_smith: (that is if you want to remove all instances of specific values)

0:49 bbloom: unless nil or false are valid values in your lists

0:49 lpvb: technomancy: can I start lein up and then issue commands so I don't have to wait for the jvm to start up?

0:49 algal: bbloom: Oh hello, this endure choppy can use postgresql as a backing store....

0:50 allenj12: ahhh kk i thought that only worked for single cases

0:50 ty

0:50 bbloom: algal: eh. if you're going to use postgres, use postgres

0:50 justin_smith: allenj12: a set acts as a function that returns its input if it is in the set, or nil if it is not in the set

0:51 allenj12: ooooo kk

0:51 justin_smith: allenj12: if nil or false are valid values that you do not want to remove, you could use contains? (remove (partial contains? (set unwanted)) all)

0:51 algal: bbloom: yeah, it's probably just write text into a field..

0:53 allenj12: justin_smith; no the first will do but that is good to know ty very much

0:54 algal: bbloom: great post, btw..

0:54 bbloom: algal: thanks

0:55 algal: I totally hear you w/r/t/ to rediscovering simplicity ten years later. :)

0:55 bbloom: oh yeah :-)

0:55 every good dev has their OOP class hierarchy days, and their IT MUST SCALE TO A BAJILION days

0:56 and a few other phases w/ perfect coding standard and whatnot

0:56 but clojure is about getting shit done & in this room, whether or not you're using clojure or the cool-shit-de-jour, you're gonna get some fucking simplicity up in here.

0:56 algal: bbloom: Well, we grow up with what's in the air.

0:57 bbloom: And late 90s was full of mis-emphasis on large scale and class tangles.

0:57 anyway, that's I how remember it. :)

0:57 bbloom: algal: i recently saw some C# code i wrote while at msft. i was like "did i really document my functions like that?"

0:58 john2x: i'm trying to make a fn that gets synonyms of a words via an api, but it seems the `for` form isn't going through `words` (http://vpaste.net/AH9fD)

0:58 bitemyapp: my code 6 months ago was terrible.

0:58 bbloom: john2x: for is lazy

0:58 john2x: you should not have side effects inside lazy operations

0:59 algal: my code 6 days ago did not think through data consistency. so we you never stop learning. maybe because I never stop forgetting. :|

0:59 bbloom: john2x: move your swap outside the for

0:59 you can do something like: (swap! word-syms assoc (for ... ))

0:59 or rather:

0:59 (swap! word-syms assoc (concat (for ... ))) where your for returns [key value] pairs

1:00 er dur, with an apply!

1:00 amalloy: bbloom: (apply assoc! ...)

1:00 er, apply swap!

1:00 bbloom: (apply swap! word-syms assoc (concat (for ...)))

1:00 amalloy: although i don't know why you'd use apply assoc rather than into

1:00 bbloom: amalloy: lol duuuh

1:00 (swap! word-syms into (concat (for ...)))

1:01 or no concat

1:01 amalloy: bbloom: (= (concat x) x)

1:01 bbloom: ignore me

1:01 i'm totally dumb right now

1:01 sorry, i failed miserably. that means it is way past bed time

1:01 john2x: listen to amalloy. he seems to be awake

1:01 john2x: thank you both :)

1:01 bitemyapp: ddellacosta: I figured out my problem. I forgot how POSIX works and now realize I have to just nuke all waiting promises whenever anything fails.

1:01 aka, the nuclear option.

1:02 ddellacosta: bitemyapp: d'oh.

1:02 bitemyapp: This is a neutral result in my book. OTOH, the positive thing that happened today is that I started using Linux and XMonad again today for coding. What a joy.

1:03 ddellacosta: basically the reason one was able to associate a particular point in time for the failure with a particular write with previous db client implementations is because of the blocking.

1:03 the async nature of my implementation means "one or more" writers will get an exception.

1:04 interestingly, because of the constant reader thread, a user could actually notice the agent was killed off before even attempting the write if they wanted.

1:04 so in actuality, its "0 or more" writes will fail.

1:04 which is interesting.

1:04 this error scenarios beg for a goddamn monad.

1:20 algal: bbloom: been browsing through your archives, especially your old Rails stuff from 2010. Curious if you have any favorites for CLojure-based webdev, or if you think there's nothing competitive.

1:20 bitemyapp: there are a lot of people here happily doing Clojure web dev.

1:21 you might ask if any of them have a setup that works for them instead of seeking a negative result.

1:21 algal: bitemyapp: what are most of them using, is my question. Just building directly on top of ring/compojure? or ... ?

1:21 bitemyapp: algal: ring/compojure is the most popular. a good "getting started" setup for that is Luminus. http://www.luminusweb.net/

1:27 algal: bitemyapp: Not seeking a negative result, but I am interested in knowledgable comparison. My impression is Rails is a lot more mature for webdev stuff but I've never actually used it.

1:27 But I can see how it's a sensitive topic so maybe I'll just shut up. :)

1:28 bitemyapp: algal: it's not a sensitive topic, just trying to provide methodological guidance.

1:28 algal: Clojure's current web stack lends itself very well to data heavy projects.

1:28 stuff that is mostly samey-samey web forms and the like Rails might be more productive for, but there's a lot of opportunity to abstract stuff like that away in Clojure - partly due to being a Lisp.

1:30 algal: bitemyapp: by data-heavy do you mean a web service that does a lot of actual computation, or do you mean stuff that handles a lot of data in the browser?

1:32 bitemyapp: algal: both, anything where you've got a lot of cross-talk between server and client too.

1:32 justin_smith: algal: I think data heave means large amount of data being summarized or operated on in some way

1:33 bitemyapp: algal: yes, what justin_smith said.

1:34 algal: ok. thanks for the perspective.

1:38 bitemyapp: sweet, my error handling works.

1:39 justin_smith: bitemyapp: nice, how did you work it out?

1:39 bitemyapp: 勝!

1:40 justin_smith: there's a finalizer message that gets sent to the agent, that finalizer nukes the promises (delivers exception) and then the agent itself (reraises)

1:41 the "run" function throws if the promise is an instance of java.lang.Exceptio

1:41 and I'm adding an "agent dead?" check as well.

1:41 TEttinger: bitemyapp, if I order that at a chinese restaurant, will i get kicked out?

1:41 bitemyapp: there's a possibility for a race condition here but I've got a timeout and a secondary check that I'm adding.

1:41 justin_smith: you would think there would be something more concise for such a common thing

1:41 bitemyapp: TEttinger: it's katsu, the japanese for victory. So...I don't know?

1:42 justin_smith: hum. yeah there's no good way to do this, mostly because people use sockets in a synchronous way usually.

1:42 TEttinger: ah, I saw many little strokes and thought chinese

1:42 bitemyapp: justin_smith: so its expected your synchronous reader/writer will have knowledge of direct causality.

1:42 justin_smith: TEttinger http://en.wiktionary.org/wiki/%E5%8B%9D

1:42 bitemyapp: justin_smith: the way I designed revise's conn mgmt uses sockets MUCH more efficiently but there's some non-determinism here.

1:42 TEttinger: I have had chicken katsu now that I think about it

1:43 bitemyapp: TEttinger: that's a japanese dish though :)

1:43 TEttinger: indeed

1:43 bitemyapp: the timeout + re-check for error handles the race condition scenario but I'm not totally happy with it even if the likelihood of encountering it is minimal.

1:44 I guess this comes with the territory of async + using socket resources in a semi-novel way.

1:44 justin_smith: TEttinger: found that via wiktionary, via a google search, seems it is victory in han and kanji

1:44 TEttinger: it looks like a lot of strokes

1:44 justin_smith: we just need an immutable and atomic network layer

1:44 and these concerns are obviated

1:45 algal: bbloom: also liking this one on ORMs and declarative schemas.

1:46 justin_smith: TEttinger: any golfer will tell you that victory comes after many strokes, I just copied and pasted the character into my search bar in my browser

1:46 TEttinger: haha

1:49 bitemyapp: `cbp: around?

1:53 `cbp: bitemyapp: kinda what's up

1:53 bitemyapp: oh you got the error handling down? nice

1:54 jared314: when connecting datomic to a heroku postgres instance, how do you add the NonValidatingFactory?

1:54 i tried the db params but it still gives me a cert error

1:57 bitemyapp: `cbp: yeah it works, but there are more changes to the API

1:57 `cbp: breaking ones, unless you object.

1:57 `cbp: run is derefs promise with a timeout and checks for errors by default, run-async just returns the promise, no error handling.

1:58 `cbp: ordinarily I wouldn't have fucked with run, because I know it's *everywhere*, but the safety really commends the change.

1:58 `cbp: so the previous run is now run-async?

1:58 and run blocks?

1:58 bitemyapp: `cbp: yep and yep, but run blocks with a timeout that can be overridden.

1:58 `cbp: want me to just push?

1:59 this also means query results don't need to be deref'd anymore.

1:59 `cbp: bitemyapp: yeah just push its not very breaking

2:01 bitemyapp: `cbp: let me investigate one other thing real quick, to see if I can come up with a cleaner error, then I'll push in a moment.

2:01 `cbp: ok

2:03 bitemyapp: `cbp: bja ended up getting hijacked by some important work-stuff, but possibly they can collaborate on a future project with us :)

2:06 sweet! my hypothesis was right.

2:07 `cbp: pushed!

2:07 `cbp: are you willing to fix the tests?

2:07 if not, I can get rid of the unnecessary derefs.

2:07 `cbp: its just 2 :p

2:07 ill be available later

2:08 bitemyapp: `cbp: okie dokie. I'd like a docs + tests update and a push to clojars soon. Let me know if you want help with anything, I feel a little bad dumping this on you.

2:08 `cbp: on the bright side, proper error handling now!

2:08 noodling how to handle errors in an asynchronous connection model like this wasn't super-easy :)

2:09 `cbp: its pretty original

2:09 far as i know

2:11 bitemyapp: `cbp: I'd hesitate to call it original, but it's definitely uncommon. I'm familiar with a decent number of databases and protocols and I can't think of any that do this.

2:11 `cbp: just pushed a last minute addition, reader thread kills itself off if there's an agent error.

2:12 `cbp: also, I don't really know exactly why, but when the rethinkdb server goes down, the socket receives a bunch of {} responses.

2:12 `cbp: huh

2:12 bitemyapp: I use that to detect socket death / server shutdown at the moment.

2:12 `cbp: lol

2:12 bitemyapp: specifically, to make the errors more descriptive.

2:12 it handles any source of error I can fathom right now.

2:13 when you see the logic in (defn run ...) you'll see how paranoid I'm being.

2:13 yogthos: hi!

2:15 `cbp: with that, I'm going to play some League of Legends. Ping me if you need anything.

2:18 `cbp: bitemyapp: should join us at dota sometime :)

2:21 bitemyapp: `cbp: I play DotA2, I'd love to play with you

2:21 `cbp: ping me whenever you'd like to do a game

2:24 `cbp: I mostly play league because the games finish faster than DotA2

2:24 `cbp: bitemyapp: i play with some friends and we used to lol but were bored of it now

2:24 bitemyapp: `cbp: well you know where to find me :)

3:10 muhoo: what does this weird destructure form do? (let [{[foo] :bar} mystery] ..)

3:12 i've seen {:keys [foo] :as bar} many times, but not {[foo] :bar} before

3:13 noidi: muhoo, {local-name :key} takes the value under :key from the map, and assigns it to local-name. local-name may itself contain further destructurings

3:13 so, in your case if the map looks like {:bar [1 2 3]}, the value of foo will be 1

3:14 because of the destructuring (let [[foo] [1 2 3] ...)

3:26 wei: why doesn't this try/catch block work? https://gist.github.com/yayitswei/7395401

3:27 oh-- nevermind, it actually does work!

3:55 BlackBlock: is there a lib for making partials in general ? (e.g. make partial fn with arbitrary arg positions)

3:58 bitemyapp: BlackBlock: there's not, and you should avoid that. The general pattern you're seeking is currying.

3:59 BlackBlock: currying is something you should look into. core.reducers had a defcurried. Also, you probably want closures.

4:16 brainproxy: state monad (protocol-monad) + core.async + trampoline = neato

4:18 danielszmulewicz: Is ritz (the debugger) a dead project?

4:19 bitemyapp: danielszmulewicz: nope.

4:19 danielszmulewicz: bitemyapp: Has it been updated to work with cider?

4:21 Did you guys already update from nrepl.el to cider?

4:22 Last time I tried (on day 1 of the cider release) there were too many problems and I reverted back to nrepl.el.

4:22 Things are stable now?

4:23 ambrosebs: dobry-den: byte array should be (Array Byte)

4:29 bitemyapp: danielszmulewicz: that's on Cider, not Ritz.

4:29 danielszmulewicz: don't use cider if you aren't willing to fix things yourself.

4:31 danielszmulewicz: bitemyapp: are you using Ritz?

4:33 bitemyapp: danielszmulewicz: occasionally, yes.

4:34 danielszmulewicz: bitemyapp: in Emacs?

4:34 bitemyapp: yes...

4:35 danielszmulewicz: are you wih nrepl.el or cider?

4:35 bitemyapp: danielszmulewicz: I'm going to bed momentarily, you have about 2 minutes to ask a useful question.

4:35 danielszmulewicz: bitemyapp: good night

4:35 bitemyapp: nrepl.el, because I'm not dumb enough to use cider while it's broken as fuck.

4:35 danielszmulewicz: bitemyapp: that's what I'm asking

4:36 bitemyapp: I've seen some enthusiastic praises for cider on the Clojure community on Google+, so I was wondering...

4:36 bitemyapp: cider is just nrepl.el, but with all the integration broken because shit is half-renamed.

4:37 there's nothing to praise.

4:37 I have no idea why you would take sycophantic comments on Google+ seriously.

4:37 instead of actually looking at what was done

4:38 danielszmulewicz: bitemyapp: You made your point. You must be tired. Sleep well.

4:46 bitemyapp: ~cider is Cider is unstable and broken right now, most things don't work. Use the last stable version of nrepl.el for now.

4:46 clojurebot: You don't have to tell me twice.

4:46 bitemyapp: ~cider

4:47 clojurebot: cider is rage-inducing

4:47 bitemyapp: ~cider

4:47 clojurebot: cider is rage-inducing

4:47 bitemyapp: ~cider

4:47 clojurebot: cider is Cider is unstable and broken right now, most things don't work. Use the last stable version of nrepl.el for now.

4:47 bitemyapp: very good.

4:47 ~cider

4:47 clojurebot: cider is rage-inducing

4:47 bitemyapp: lol :)

6:19 allenj12: is there an easy way to go from a 2d list to a 1d list

6:23 nvm got it (apply concat)

7:07 rrc: I was struggling to determine the cause of a "CompilerException java.lang.NullPointerException, compiling:(form-init3395114284023394383.clj:1:8)" exception I was getting. Ultimately I found nil being passed as an argument to -, but it was only down to tedious commenting out and printlns. Is there are better way to debug these sorts of issues? Why didn't tools.trace work in this case?

7:15 john2x: curious, is this fn in good form/idiomatic? http://vpaste.net/R8AH5 using an atom for mutation in a for loop.

7:17 dobry-den: john2x: no

7:18 john2x: hmm. what would be a better way to achieve what I want?

7:18 dobry-den: yeah sorry, i pressed enter too soon.

7:19 first of all, im pretty sure that :let (in the for-loop) is run every iteration

7:19 so that slurp is being run

7:20 john2x: yes, that part's intended. it's calling an api request for each `word`

7:20 dobry-den: oh i see

7:20 it's an http request

7:20 haha

7:20 john2x: yes :) it's an api to a thesaurus

7:21 dobry-den: the first thing youd want to do is make many requests at once

7:21 pretty sure that's gonna wait for the full slurp on eeeeevery word

7:23 Continuing on our naive path, an easy change would be to turn the atom into an agent

7:24 and use send-off in the loop

7:25 http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/send-off

7:26 john2x: ah, I kinda need the fn to be synchronous. :) it's basically to get synonyms of a list of words.

7:26 dobry-den: does each request have to be synchronous?

7:27 the idea is that you make all the requests asynchronously, but synchronously wait for them all to complete

7:28 john2x: nope. ah, I think I follow now. so I'll just realize the agent and then start parsing the responses?

7:29 dobry-den: yeah. although still not an ideal solution, but it came to mind an incremental improvement

7:30 you could probably even make a trivial change to use `pmap` and begin processing immediately, but it's more for cpu bound things

7:34 unfortunately youre not talking to the parallel programming guru here.

7:35 john2x: thanks. improvement is improvement :) what about the part of using an atom as a variable for keeping track of a mutating object while iterating/looping?

7:36 dobry-den: like i think a simple solution could be to map a function across your words that returns an async promise/future (dunno which one is which) of the request, then lazily consume them with pmap

7:36 Morgawr: is there a reason why "send" and "send-off" functions in clojure don't end with a '!' ? It feels a bit inconsistent because, as far as I understand, they have side-effects and are not that different from swap! or reset! or similars

7:37 dobry-den: john2x: in general you want to use loop/recur and update the loop binding when it's sequential/synchronous stuff

7:38 but of course here we'd wanna coordinate work across threads

7:38 hyPiRion: Morgawr: Probably same as why alter, commute, ref-set and ensure doesn't end with an '!': Different semantics within a transaction I guess.

7:38 dobry-den: john2x: also, iirc, the swapping an atom can actually run the function multiple times

7:39 it does something called compare-and-set

7:40 since you dont actually care what order the requests conjed to the ref, atom doesnt make sense

7:40 Morgawr: hyPiRion: but agents don't have to run inside a transaction though

7:40 hyPiRion: I know

7:40 Morgawr: but yeah, just wanted to know if there was any explicit reasoning behind this or if it's just an occurrence

7:40 * hyPiRion shurgs

7:40 dobry-den: now u got 'im shurgin

7:40 hyPiRion: *shrug, heh

7:40 Morgawr: lol

7:41 dobry-den: Morgawr: Only Hickey Knows

7:41 hyPiRion: It's probably not too far off from what you said, dobry-den, something about no retries

7:42 dobry-den: i worked in a ruby codebase where one contractor before me used the bang! to mark destruction. of course, it's a monolithic rails app. why doesnt every method have a bang on it? only the code he wrote had them.

7:42 so every day was this constant game of trying to remember which functions had the bang

7:43 hyPiRion: haha

7:43 dobry-den: sort of like being stuck in a hell where you're trying to plug in usb drives the right way every time

7:44 Morgawr: damn 4-dimensional usb sticks

7:45 hyPiRion: I wish they were something like a CAT cable with a little bump up on one side

7:45 dobry-den: i wish it was like, a triangle

7:45 er wait

7:45 why not just a circle

7:45 hyPiRion: yeah, hah

7:45 Morgawr: I'd just take it like a normal audio jack

7:45 just plug it in and forget about it

7:45 dobry-den: that's not how you make money

7:46 Morgawr: is that why I'm poor? :(

7:46 dobry-den: :(

7:46 john2x: or PS/2. now that was hard to get right

7:46 dobry-den: haha

7:46 on the dark side of a computer buried on the underside of a desk, yeah

7:50 john2x: i often use http://http-kit.org/client.html for http requests which makes async stuff easy

7:52 john2x: dobry-den: thanks for help. :) I'll finish the rest of my project first then go back to improving.

7:53 dobry-den: yeah that's a trait i would like to have

7:54 but the yaks arent gonna shave themselves, i tell myself

7:55 john2x: and yet here I am asking about whether a fn was idiomatic with still a lot left to finish ;P

8:05 dobry-den: it would be nice if clojure had a batteries-included way to consume the first N elements of async jobs.

8:07 basically, a pmap for i/o bound jobs

8:09 the problem with pmap for i/o bound work (and work in general, really), is that you have no control over how many items it's realizing at once,

8:10 and, worst of all, even though it will realize [a b c d e ...] at once, if b c d e take 1 second but a takes 30 seconds, it will wait 30 seconds before consuming any more elements

8:11 i'm gonna write it right now ^_^

8:11 pooled-map

8:14 dont try and stop me cuz u cant

8:45 opqdonut: dobry-den: seque does almost that

8:46 dobry-den: We have a custom version of pmap that takes an ExecutorService (i.e. thread pool)

8:46 ... which is useful

9:23 CookedGr1phon: Hi, I'm doing some stuff which requires a few small bytebuffers, and it's really annoying that their tostring gives me useless info like #HeapByteBuffer [lim=0, size=16, ...]

9:24 so what I really want to do is override the tostring, but I'm creating it from a static allocate method

9:24 and it's all naff and convoluted

9:24 is there any other way to override the str output to give me a useful dump instead?

9:24 or am i stuck going down this route

9:24 (hoping someone's going to tell me about a handy to-string protocol or something)

9:35 what is it that appends the #<Classname ... > part of the output?

10:22 noidi: CookedGr1phon, you can't customize str output, but you can define a custom print-method http://clojuredocs.org/clojure_core/clojure.core/print-method

10:23 and then use pr-str to create a string representation of your buffers

10:26 CookedGr1phon: genius!!! Thankyou so much

10:26 that works perfectly

10:27 exactly what I was looking for, all the stuff I care about was using pr-str under the hood anyway (i.e. test output etc)

10:29 this is infinitely more readable

10:31 justin_smith: what about print-dup?

10:32 I thought pr-str used print-dup

10:33 with *print-dup* true (which indicates you are trying to write a readable version of your thing), it uses print-dup instead of print-method

10:35 (the chain of calls going down being pr-str -> pr -> clojure.core/pr-on

10:36 )

10:38 CookedGr1phon: what practical difference does this make? when is pr-str meant to be used vs print-dup, i can't really tell the difference from the docs

10:39 other than print-dup only does it when you tell it to use that representation specifically?

10:40 justin_smith: pr-str is meant to be readable

10:40 err

10:41 print dup is called by pr-str

10:41 CookedGr1phon: and print-dup is meant for serialisation?

10:41 justin_smith: if you set the *print-dup* special

10:41 otherwise it calls print-method

10:41 the first is if you want something that is loadable from a string to get the same thing back

10:42 the latter is meant to be readable

10:42 CookedGr1phon: okay, I only care about looking at this, i'm not trying to serialize, there's much better serializations for this data

10:42 justin_smith: check out (source clojure.core/pr-on), which pr-str ends up calling a couple of levels down

10:42 yeah, pr-str calls it if you set that special

10:43 but if all you care about is readable output, setting print-method should suffice

10:44 CookedGr1phon: cool

10:44 thanks for the clarification

11:06 CoconutCrab: free

11:06 ops, sorry, wrong window

11:14 justin_smith: https://www.refheap.com/20657 any hints why this doesn't print anything but a blank line?

11:15 updated to show a call that should output but does not


13:12 sritchie: any liberator users?

13:12 I'm trying to return a 404 not found page for a resource,

13:12 for js, css or img

13:14 `cbp: bitemyapp: ok i added some docs and fixed the tests and deployed 0.0.4

13:16 bitemyapp: let me know if you want me to add something else to the docs

13:17 edoloughlin: Anyone using cider? I have a repl buffer and a *cider-error* buffer but there's nowhere I can see my stdout (i.e., my log output)

13:17 danielszmulewicz: sritchie: liberator user here

13:18 sritchie: I think I just found my answer: liberator.representation/ring-response

13:18 for returning a different content type from, say, a js request

13:18 danielszmulewicz: sounds about right

13:18 indeed

13:18 sritchie: the other Q I had

13:19 was about how to stack behaviors

13:19 say I want to share authorization logic,

13:19 but have everything else be different

13:20 danielszmulewicz: liberator has :authorized? which you put everywhere where you need to authorize a resource

13:21 Does that address the question?

13:21 sritchie: well, yeah -

13:21 danielszmulewicz: OK, cool

13:21 sritchie: but is the pattern to make a function taht returns a resource?

13:21 (defn get-resource [auth-fn] (resource :authorized? auth-fn ….))

13:22 danielszmulewicz: or is there a way to compose the two

13:22 danielszmulewicz: no, the pattern is to write a function to return a boolean

13:22 *that*

13:23 sritchie: danielszmulewicz: yeah, for authorization

13:23 danielszmulewicz: sritchie http://clojure-liberator.github.io/liberator/doc/decisions.html

13:24 sritchie: the question is how to nest them

13:24 for example, say I have the same handle-not-found logic for every one of my resources

13:24 since these are all constructed like maps,

13:24 it would make sense to be able to merge them, for example

13:24 danielszmulewicz: you don't nest anything, that's the beauty of liberator

13:24 you have a decision graph

13:25 sritchie: but you duplicate quite a bit if you have 10 resources with the same handle-not-found fn

13:25 danielszmulewicz: anywhere in the chain you can change the outcome of the request

13:25 sritchie: yup, got that part, it's really awesome

13:25 what I want to do is lock down a bunch of those decision points,

13:25 danielszmulewicz: if you have

13:25 sritchie: and then write resources that fill in the rest

13:25 same auth, same :handle-not-found

13:26 danielszmulewicz: you reuse the same function

13:26 that's not called duplication

13:27 sritchie: but every resource has :handle-not-found handler

13:27 when you could imagine something like

13:27 (defresource my-resource :base-resource something-else :handle-ok ....)

13:27 like, a base-resource key

13:28 danielszmulewicz: and then you'd just merge the two maps to create the final resource

13:28 if you've got 10 decision points that you want to share for every resource,

13:28 danielszmulewicz: I think you can do something like that with the routing but I don't know exactly how

13:28 sritchie: I would argue that it's not clean to have to copy-paste those ten lines

13:28 those 10 key-function pairs

13:28 okay, liberator routing?

13:29 danielszmulewicz: it's the compojure part where you can generalize routes

13:29 amacdougall: Here's a new one -- I've been using {:optimizations :whitespace} for ClojureScript dev, but when I switch it to :none, I get "goog is not defined" at the very start of my main.js. The exact line that causes it is: goog.addDependency("base.js", ['goog'], []);

13:29 Anyone know what's up?

13:29 I'm on ClojureScript 2030.

13:29 sritchie: oh, then internally start switching again, in handle-okay, using compojure?

13:29 yeah, I guess you could use a resource as a middleware

13:29 interesting, that would work

13:29 danielszmulewicz: yes, liberator is ring compliant and I think you can sudy compojure addanced routing

13:29 *study*

13:30 sritchie: sweet, I think that's the right call. thanks guys

13:30 danielszmulewicz: *advanced*

13:30 sritchie: thanks danielszmulewicz, that is :)

13:30 danielszmulewicz: sritchie: cool, good luck

13:32 amacdougall: I have a hunch that since I'm using some Google Closure libraries, my app will only work if it's fed into the Closure Compiler... and I guess specifying no optimizations skips that stage? You'd think there would be some provision for that though.

13:40 Hm, look like it might be fixed by this commit: https://github.com/clojure/clojurescript/commit/dbf31380b925815d832a6cc53364d063a59dafad

13:56 rabidsnail: What's the idiomatic way to do argmin?

13:57 justin_smith: rabidsnail: waht is argmin?

13:57 n/m wikid it

13:59 rabidsnail: I mean I could do it with loop/recur, but it seems like there should be a cleaner solution

14:00 dobry-den: I need to parse "little-endian variable-length integers where the MSB determines the sign of the integer", but for some reason i just can't figure out how to write a function that turns 0x81 into -1, and 0x80 into -0.

14:00 justin_smith: rabidsnail: I am putting together a version with reduce, assuming args are a sequence to iterate

14:00 generating that sequence is a separate problem I think

14:01 rabidsnail: justin_smith: yeah, I'd expect the signature to be (argmin function sequence)

14:02 justin_smith: (defn argmin [[arg & args] f] (first (reduce (fn [[arg min] arg] (let [v (apply f arg)] (if (< v min) [arg v] [arg min]))) [arg (f arg)] f)))

14:02 maybe

14:02 that allows args to be more than one arg, just put each set of args in an array or vector

14:03 rabidsnail: right, that'd work

14:03 I'm kind of surprised there's nothing like that built in

14:04 it's a really common thing to want to do when translating math out of papers

14:04 justin_smith: though I used the name "arg" one too many times

14:08 rabidsnail: https://www.refheap.com/20677

14:08 my go at it

14:09 if the function is always single argument you can simplify it a bit by taking out the apply usage

14:10 also the order of arguments should be reversed so the sequence is last

14:10 that is the normal order for clojure

14:10 CookedGr1phon: (max-key (comp - f) a b c d ...)

14:11 oh

14:11 or min-key

14:11 that would also work

14:12 justin_smith: CookedGr1phon: awesome, I did not know about max-key

14:12 CookedGr1phon: it's a bit weirdly named

14:12 justin_smith: or min-key!

14:12 nice

14:12 TIL

14:13 arrdem: clojurebot: ping

14:13 clojurebot: PONG!

14:13 justin_smith: rabidsnail: so your assumption was correct, it did exist, and it had the most intuitive name :)

14:13 *second most intuitive

14:14 rabidsnail: CookedGr1phon: thanks

14:14 CookedGr1phon: np

14:14 justin_smith: yeah, why think in terms of keys rather than functions?

14:15 cYmen: hm... I have created a clojure project using leiningen and am editing the src file with vim including fireplace. How do I trial run my app?

14:15 justin_smith: also it runs f twice as often as my version does, looking at the source :)

14:15 cYmen: I'm just trying to learn how to use ring so I suppose I should start a repl and start a server but what is the best way to do that?

14:16 justin_smith: I like my version better, it calls the function half as many times and allows for multiple arguments to the function :P

14:17 cYmen: using ring, I usually fire up 'lein ring server' and start an nrepl from inside the ring process

14:18 that way I can interact with variables inside the process that serves requests (ie. save a request or intermediate value in an atom so I can play with it in the repl)

14:18 the github page for nrepl shows how to start the server from inside your app, it is straightforward

14:20 cYmen: justin_smith: So the "lein ring server" part I think I have configured and it seems to work. How do I start an nrepl inside the ring process?

14:22 justin_smith: cYmen: as I said, it is mentioned on the github page: https://github.com/clojure/tools.nrepl

14:22 (defonce server (start-server :port 7888))

14:22 cYmen: uh yeah already copy pasting stuff

14:22 justin_smith: you would run that inside the init function of your ring app

14:23 cYmen: init function...

14:23 *sigh*

14:23 There is so much STUFF to help you get started with clojure and so little explanation of that stuff I feel like I am getting dumber.

14:24 justin_smith: the init function being the one that is passed as :init to the :ring key in your project.clj

14:24 and that is where you define it if you don't have one already :)

14:24 cYmen: apparently I don't

14:25 justin_smith: (defproject ... :ring {:init your.ns/your-init-f})

14:25 that's all you need

14:26 well the stuff in the ... is assumed to already be in there :)

14:26 cYmen: Yeah, I got that part. :p

14:26 justin_smith: cYmen: there is a bootstrapping period, after which the help all starts to make sense

14:26 in my experience

14:27 what I am saying is if you stick to it you will get there

14:27 and feel free to ask clarifying questions if my help isn't helpful

14:28 I can share a project.clj of mine if you want to see what I mean regarding how the :init fn is defined

14:28 cYmen: I should be able to get that right, let me check. ;)

14:30 justin_smith: once you have the init function set up, and you start the nrepl server in that, you should just be able to connect to that port with fireplace

14:30 cYmen: So ....uh if I did it right how do I know?

14:33 justin_smith: it will open the port, you can connect to it with 'lein repl :connect <port>' in a terminal

14:33 if that works, next step is making fireplace connect to that port

14:34 yedi: anyone know of langs where 0 is falsy and "" is not? (or vice versa)

14:34 justin_smith: so if you pasted that code, 'lein repl :connect 7888'

14:35 xuser: yedi: C ?

14:36 cYmen: justin_smith: It keeps telling me "Port is required"'

14:37 swarthy: cYmen: if you don't mind spending a bit of money 'Web Development in Clojure' by yogthos (in this channel) is really good at explaining all this stuff.

14:38 justin_smith: xuser: I think "" is a NULL pointer in C, which is usually 0, which is falsey

14:38 mtp: justin_smith‘ the empty string is not a null pointer

14:38 justin_smith: the C spec says any value can be NULL for a specific platform, but 0 is normal

14:38 BUT

14:39 cYmen: lein repl :connect 7888

14:39 or whatever port number you told the server to open on

14:40 cYmen: justin_smith: I think my irssi just crashed, sorry.

14:40 justin_smith: LEIN_REPL_PORT=7888 lein repl :connect works. See https://github.com/technomancy/leiningen/issues/1344

14:40 amacdougall: bja just helped me out big time with my ClojureScript issues -- thanks!

14:41 justin_smith: mtp: what about an empty string const literal?

14:42 that would be the value 0 no?

14:42 mtp: justin_smith‘ still not a null pointer

14:42 that gets allocated in rodata iirc

14:42 justin_smith: though the address of that string is not likely to be 0, it will have 0 as its first value

14:42 mtp: exactly

14:43 the name of the thing, is not the thing

14:43 justin_smith: which is the value you get if you deref that location

14:43 mtp: but that still isn't a null pointer!

14:43 if you deref it, you get (char)0

14:44 a null pointer is (char *)0

14:44 justin_smith: cYmen: oh yeah, I have been avoiding upgrading my lein :)

14:44 xuser: justin_smith: https://www.refheap.com/20680

14:45 justin_smith: mtp: implementation wise the first value you read when you read something that is an empty string will be a 0

14:46 mtp: justin_smith‘ but according to the type system, that is still not a pointer to null

14:46 it is a null byte

14:46 cYmen: justin_smith: So how do I connect to that using fireplace? I can't seem to do it with :Connect.

14:46 justin_smith: yeah, I was wrong about the NULL thing, that is also usually 0, but beside the point

14:46 yeah

14:47 indeed

14:48 mtp: as I said above "though its address won't be 0, its value when you deref will be"

14:48 mtp: justin_smith‘ it will be a literal 0 byte, which, as you mentioned, may not be the representation of a null pointer on your platform

14:48 (void *)0 is not ever guaranteed to be (char)0

14:49 (it *frequently is*, on today's machines)

14:50 rabidsnail: Is there a way to say "here is a value c which is the same as value a except use value b for comparison purposes" (where a is, say, a custom hash)?

14:50 like, is there a protocol for comparisons?

14:51 justin_smith: xuser: fair enough, this is the version that does what I was thinking: https://www.refheap.com/20681 which of course is not standard C at all

14:51 mtp: imagine a machine where pointers are themselves typed - a null pointer could be the bit pattern 0x01 in memory, but C guarantees that when you type (void*)0 you get a null pointer.

14:51 :)

14:51 justin_smith: mtp: yeah, I said a couple times I was totally wrong about that null thing already

14:52 mtp: tl;dr there are a lot of subtleties here

14:52 justin_smith: agreed

14:52 mtp: http://thoughtmesh.net/publish/367.php

14:52 justin_smith: I only briefly implied otherwise, and have since corrected myself

14:53 cYmen: justin_smith: Actually..when I have that repl connected how can I interact with the ring server and do stuff like setting a var, redefining a function or reloading the source?

14:55 justin_smith: mtp I actually wouldn't be surprised if that existed somewhere

14:56 mtp: justin_smith‘ yeah, it is perfectly legal C, but it breaks the assumption that (char)0 == (void *)0 :)

14:57 (upon which we now agree)

14:58 justin_smith: cYmen: simple example: in an ns which serves the request, do (defonce requests (atom [])), then in the context where you use the requests (swap! requests conj request) - this is all done in your source. Next, from the repl you can access the requests (deref your.ns/requests) and access values in them etc.

14:59 or, in the repl, you can switch to one of your existing namespaces (ns your.ns) and redefine functions with defn, or call those functions, etc.

15:00 mtp: fascinating article

15:01 xuser: justin_smith: your example is valid C but you are checking against an element of the array (the null character), if my example we are checking against a pointer (address).

15:03 s/if/in/

15:05 justin_smith: xuser: true

15:05 it was a last ditch attempt to present that some aspect of what I was saying was not complete bullshit :)

15:06 not that I got out of it with any dignity intact

15:06 xuser: ;)

15:08 justin_smith: mtp: "In fact, since punning always involves the representation and not the semantics, it is only truly possible outside of the defined semantics of a given language. In most languages, what this means is that type punning is simply not possible."

15:08 mtp: justin_smith‘ do you agree or disagree?

15:09 justin_smith: mtp: I have, for a personal project, taken Doubles, put them in a byte array, then extracted longs out of the byte array

15:09 I wonder if that is a counter example to what he is saying? clearly I was type punning, if in a roundabout way

15:09 mtp: that is an explicit operation that converts your doubles into C's native memory model, and then out again

15:10 so you were type-standup-comedying. :)

15:10 justin_smith: (this was done because I wanted to get the set / unset bits in the doubles, to debug deserialization from a binary file generated by a C program)

15:13 that it is not possible in most languages? I would say rather that it is not easy in most languages.

15:14 mtp: it requires two conversions

15:14 instead of directly accessing the representation

15:14 justin_smith: hah

15:14 mtp: this is the point of the article!

15:14 justin_smith: yeah, there are intermediate steps in my clojure version that don't need to happen in c

15:14 mtp: to get this behavior out of most languages, you have to do work to get things into a bytearray

15:15 c's memory model IS a byte array

15:16 justin_smith: mtp: it is really really weird to see Derrida referenced in a paper about C

15:19 OK

15:19 mtp: :)

15:21 cYmen: justin_smith: How do I redefine them all by re-reading the source after I have made changes?

15:29 justin_smith: cYmen: (require 'your.ns :reload)

15:30 from the repl

15:30 cYmen: I ran this http://pastebin.com/pMBEREVJ with lein ring server-headless and then redefined handler2 in a connected repl. Why doesn't it change the output when I reload the site?

15:31 justin_smith: Thanks, that is super useful.

15:35 justin_smith: cYmen: because ring is likely not looking up the value of handler on each request

15:35 there is a trick using #'handler which forces it to resolve it on each new request

15:36 cYmen: hm

15:36 justin_smith: also there is middleware like wrap-reload that automatically reloads any changed files on each request

15:36 cYmen: don't say that yet, I am still forgetting exactly where you specify #'handler instead of handler - I have a framework I use that does a bunch of these things for me

15:37 I only know the details because I helped write the framework

15:37 cYmen: I think I just want to reload the entire file every time.

15:38 Coding in the repl isn't pleasant anyway.

15:50 justin_smith: cYmen: it can be with proper editor integration

15:50 but yeah, there is the wrap-reload handler if you want that

15:51 cYmen: I'm actually fine with manually reloading if it is just two keypresses in the repl.

15:51 justin_smith: http://clojuredocs.org/ring/ring.middleware.reload/wrap-reload

15:56 uparrow / return

16:10 TheLastProject: Hey everone. I'm trying my hand at Clojure, and this is my first day playing with it, but I'm running into an issue. My idea was to have the user-input call similarily-named functions. If the user entered "list upper" it should call "list-clothing" with parameter "upper". I've been playing with this for hours, but I can't get it working. Here is my code: http://pastebin.com/sMapEQ5C. The error I get is

16:10 "clojure.lang.PersistentList$EmptyList cannot be cast to clojure.lang.Symbol" if I use no parameters and "clojure.lang.PersistentVector$ChunkedSeq cannot be cast to clojure.lang.Symbol" if I use a parameter. The problem is in the last line, at the (rest ...) part, it seems. I was wondering if anyone could figure out what I was doing wrong?

16:16 justin_smith: TheLastProject: why does add-clothing execute y as a function?

16:18 TheLastProject: justin_smith: I thought that was supposed to add y to the x vector, although I must admit that I haven't tested add-clothing yet, I wrote it but went to write the resolving of user input before giving it a try

16:19 justin_smith: also, your call to into for add-clothing does not change your clothing list, it only returns a copied and modified version of it that you don't do anything with

16:19 TheLastProject: justin_smith: Ah, that is what the whole "unmutable" deal was about. Okay, good to know that needs fixing too

16:23 justin_smith: TheLastProject: vectors are not mutible

16:23 you can return a modified version, and replace a binding with that value

16:23 but the vector itself never changes

16:24 TheLastProject: justin_smith: Yeah, that's probably what I need to do to get add-clothing to work properly. Still wonder how to get the (resolve ...) part working, hmm...

16:24 justin_smith: I am working on a slightly easier to debug version of the last part of the code

16:24 TheLastProject: Ah, okay

16:24 justin_smith: example:

16:25 TheLastProject: I've tried literally everything there, heh...

16:25 justin_smith: ,(let [x [1 2 3] y (into x [2])] [x y])

16:26 clojurebot: [[1 2 3] [1 2 3 2]]

16:26 justin_smith: notice that x is unchanged, but the new binding, y, shows the result (once clojurebot responds)

16:26 * TheLastProject nods

16:26 TheLastProject: So for add-clothing I need to overwrite the old x with the new y

16:30 justin_smith: Someone else managed to figure out the last line. I had to get the (rest) part outside of the resolve, to call the function with the argument

16:30 At very least, thank you a lot for the heads-up on the add-clothing part, that would've really caused a headache otherwise

16:30 coventry: TheLastProject: clojure actually makes it fairly awkward to overwrite variables like that, because it discourages use of mutable state. If you want to do that, you should probably put x in an atom.

16:30 TheLastProject: Wait, no

16:31 justin_smith: TheLastProject: are you running the code from the same namespace where it is defined? if not the resolve will fail

16:31 TheLastProject: The resolve didn't work >.<

16:31 justin_smith: I have no clue how namespaces work, honestly

16:31 justin_smith: took him long enough!

16:32 TheLastProject: coventry: Atom. Need to read up on that. Thanks

16:32 OtherRaven: probably should read up on namespaces too :p

16:33 TheLastProject: OtherRaven: Heh, obviously, although I've tried that before back in my Python days and never managed to grasp the concept, so I'm not very hopeful about being able to grasp it this time, but we'll see

16:33 coventry: TheLastProject: I would drop the attempt to resolve the fn vars, and use a dispatch map (or multimethod, if you want another new concept for your pile.)

16:33 TheLastProject: Well, okay, it are STILL my Python days

16:33 justin_smith: not with the new y

16:33 you need to put the version with y added where it was

16:33 atoms are a standard way to do that

16:34 OtherRaven: TheLastProject: do you know how java classes work? 'cause namespaces compile down to java classes

16:34 justin_smith: (swap! x conj y)

16:34 amalloy: OtherRaven: no they don't

16:34 justin_smith: ,(let [x (atom [0 1 2] y 3)] (swap! x conj y) @x)

16:34 clojurebot: #<CompilerException java.lang.RuntimeException: Unable to resolve symbol: y in this context, compiling:(NO_SOURCE_PATH:0:0)>

16:34 OtherRaven: amalloy: really? I thought they did

16:34 TheLastProject: coventry: "dispatch map". I have a dispatch function in one of my Python applications, but no clue how that would translate to a "dispatch map" in Clojure, hmm...

16:35 justin_smith: some day clojurebot will show us that this actually updates x

16:35 TheLastProject: Would I make a map called "dispatch" which would have things like ":add add-clothing"?

16:36 coventry: TheLastProject: I.e., (def commands {"add" add-clothing "count" count-clothing}) ((commands cmd) args)

16:36 TheLastProject: coventry: Ah! Of course1

16:36 justin_smith: coventry: yeah I just showed him how to use an atom

16:37 or tried to

16:37 every top level binding (created by def or defn etc.) is in a namespace

16:38 coventry: justin_smith: Yeah I think between us we've covered the two biggest issues.

16:38 justin_smith: if you don't specify a namespace, clojure looks inside the current binding block (like let or function args), then the current namespace, then the ones you are using

16:39 yeah, coventry's idea is good

16:39 amalloy: OtherRaven: i mean, kinda? each namespace does eventually include init code, which is run when the namespace is loaded. but i wouldn't say that the class containing that code *is* the namespace

16:40 justin_smith: (get {"list" list-clothing "count" count-clothing} (first input-words))

16:40 where input-words is a local binding for splitting the input

16:41 wow clojurebot is slogging

16:41 addisaden: you need to write like this

16:41 ,(+ 1 2)

16:41 clojurebot: 3

16:41 addisaden: ;)

16:41 justin_smith: TheLastProject: I just showed you how it is done in the whole (get {...}...) thing above

16:42 addisaden: ,(get {"list" list-clothing "count" count-clothing} (first input-words))

16:42 clojurebot: #<CompilerException java.lang.RuntimeException: Unable to resolve symbol: list-clothing in this context, compiling:(NO_SOURCE_PATH:0:0)>

16:42 OtherRaven: amalloy: Fair enough. I knew there was a strong correlation between the two concepts, anyway. XD

16:42 TheLastProject: justin_smith: Yeah, I'm trying to use that, I kinda want to define it somewhere else to keep that line from getting huge. Can I ask you if I end up not getting it to work again? :x

16:45 justin_smith: ,(future "time-travel high five")

16:45 clojurebot: #<SecurityException java.lang.SecurityException: no threads please>

16:47 justin_smith: ,@(future "dereferenced time-travel high five")

16:47 clojurebot: #<SecurityException java.lang.SecurityException: no threads please>

16:47 justin_smith: addisaden: I did

16:48 addisaden: that was not an attempt to use clojurebot, it had symbols that were never defined for him

16:49 my other attempts to use clojurebot lag a lot, for some reason

16:49 maybe he thinks I am overusing his services

16:50 TheLastProject: sure - remember you can build up definitions in the let block :)

16:50 ,"Eventually"

16:50 clojurebot: "Eventually"

16:52 justin_smith: see! that was my ironic call to future above

16:53 TheLastProject: http://pastebin.com/2Se5qJ47 <- I added a dispatch feature, and it at least calls them correctly, but it doesn't seem to send the arguments correctly. For example, typing "list upper" just returns "upper" and "count upper" returns "You have 1 pieces of clothing defined in collection (upper)". Seems it sends it as "(upper)" instead of "upper", so I guess I need to join the arguments somehow, but (join " "

16:53 (rest...)) returns every letter of upper on its own line in "list upper"

16:54 I'm probably missing something really simple which I just didn't copy over correctly from what you two said >.<

16:58 amalloy: TheLastProject: you want (apply (dispatch ...) (rest ...))

17:00 apply takes a function and a list of arguments, and calls that function with the arguments "unwrapped"

17:01 TheLastProject: amalloy: The apply unfortunately seems to just split the rest part into one argument per letter. With apply, "list upper" returns every letter of "upper" on a new line. Hmm...

17:02 amalloy: mmm. then i guess i didn't read something carefully enough

17:02 TheLastProject: I wonder what exactly is going wrong here. Is "upper" a string, maybe? It should refer to the "upper" variable, so maybe that's wrong

17:03 Heh, I ought to stop trying to learn a new language in the last few hours I have in a day, debugging such issues as a new user takes too much time

17:04 OtherRaven: TheLastProject: that's what insomnia is for

17:04 TheLastProject: OtherRaven: I don't know if I can afford that, though. I'm already terribly tired every day

17:04 justin_smith: amalloy: does he? list-clothing expects a list as input

17:06 TheLastProject: justin_smith: I hope it's clear that I want to have it return the upper list defined just above it if I call it with "upper", just to make sure, because I'm starting to get too confused to read my own code >.<

17:06 justin_smith: though this will not work for add-clothing

17:06 but add-clothing is still off, because upper is not an atom

17:07 coventry: I wish things like read-line played nicer with nrepl.el.

17:07 justin_smith: (assuming upper is where you wanted to store the accumulated added clothing)

17:07 TheLastProject: justin_smith: "Because upper is not an atom" Hmm, I really need to figure out what I'm messing up now

17:07 Yeah, I do

17:07 I think I misunderstood you before

17:07 OtherRaven: TheLastProject: in that case, probably not a good idea to code before bed. I always have a horrible time sleeping if I have bugs I haven't solved

17:07 coventry: TheLastProject: http://clojure.org/atoms

17:08 justin_smith: amalloy: yes, and list-clothing takes one arg, a list

17:08 and is being called with one arg, a list

17:08 TheLastProject: OtherRaven: Too late to fix that, heh...

17:08 justin_smith: so why use apply?

17:08 OtherRaven: TheLastProject: Tehehe

17:09 justin_smith: TheLastProject: are you passing in "upper" and expecting that to resolve to the var upper?

17:09 TheLastProject: that isn't quite how clojure works, I would suggest another lookup, the same way you look up the function

17:10 if you type upper into read-line, yes, it will be a string

17:10 TheLastProject: justin_smith: That's kinda what I was trying to do. Expecting, well, I don't expect too much when I don't know how a language works. Hmm, okay, let me think for a bit

17:11 justin_smith: TheLastProject: the early mistakes are the hardest, if you stick with it it all makes sense

17:11 clojure has different rules than most languages, but its rules are more self-consistent

17:11 and it has fewer of them

17:12 OtherRaven: TheLastProject: One thing that's good to do before sleeping is reading programming manuals. *hint hint*

17:12 bitemyapp: `cbp: looks good to me, thanks!

17:13 justin_smith: TheLastProject: so going back to the issue at hand - read-line gets the string "other", because that is what came in. A regex split on a string can only return a sequence of strings

17:13 if they were implicitly interpreted you would get very weird bugs

17:13 sorry, "upper"

17:13 so if you want "upper" to mean some collection you have defined when it comes in as text from the user, you have to set that lookup up

17:13 TheLastProject: justin_smith: I understand why it can't just be interpreted like that, it's just that I sometimes wished computers would know what I MEANT to type, instead of what I actually typed :P

17:14 Ah, I got it working

17:14 I'm sure there must be a better way, but this works for the time being

17:14 justin_smith: try this: (def collections {"upper" [...]}) then you can do a lookup on the other arg the same way you would with the first

17:15 coventry: yeah, I had to modify his code to play with it at all

17:15 TheLastProject: http://pastebin.com/6vGq7tpM

17:15 Guess this is what it is for now

17:15 justin_smith: TheLastProject: an atom is used to store a value you want to update safely

17:15 TheLastProject: it seemed like upper was something you wanted to be able to update

17:16 TheLastProject: justin_smith: Yeah, I read the atom page, but my mind is unable to parse it at the moment

17:16 justin_smith: in this case, amalloy was right, you do want to use apply, so that x and y can be separate args in update-clothing

17:17 TheLastProject: Ah...

17:17 justin_smith: and then you could use a lookup map to look up the clothing collection, the same way you use a lookup map to look up the functions already

17:17 * TheLastProject undoes adding "apply" after it broke code

17:17 TheLastProject: I'm probably just too tired to figure out the right way to do this

17:17 * TheLastProject nods

17:18 coventry: TheLastProject: You'll probably have a smoother learning curve if you read existing code for a while, code particularly with commentary like in a programming manual.

17:18 justin_smith: (btw "amalloy was right" is a truism)

17:18 sure, learning takes a while

17:18 OtherRaven: yes, programming manuals are good

17:19 justin_smith: and no need to learn everything in one day, of course

17:19 * OtherRaven suggests Clojure Programming

17:19 TheLastProject: I always try to avoid those manuals because I find them boring, heh...

17:20 OtherRaven: Really? I find them entertaining.

17:21 And if there's any language that's fun to read about, it's clojure.

17:22 justin_smith: oh, you want the DWIM instruction

17:22 I think that's a perl thing :)

17:22 amalloy: justin_smith: an emacs command, too: comment-dwim

17:22 justin_smith: nice

17:22 bitemyapp: TheLastProject: Clojure Programming is a pretty good book and will make certain you don't make unnecessary mistakes.

17:23 TheLastProject: DWIN?

17:23 amalloy: aw, no such thing as unnecessary mistakes. every mistake you make while learning is something you needed to learn not to do

17:23 TheLastProject: do what i mean

17:23 TheLastProject: Heh...

17:24 bitemyapp: amalloy: I suppose, but I see a lot of people "grind" in here.

17:24 justin_smith: TheLastProject: it is a decent start

17:25 making it use an atom is as simple as wrapping the def like (def upper (atom ...)) and then using @ when reading and swap! or reset! to change the value

17:27 and check out "clojure programming" from oreilly and "the joy of clojure"

17:27 TheLastProject: Okay, I get it, I'm not getting out of reading :P

17:28 justin_smith: those books are not manuals

17:28 the manual is right in the repl

17:28 OtherRaven: the joy of clojure is nice, but too hard for a beginner

17:28 justin_smith: (doc fn)

17:28 clojurebot: "([& sigs]); params => positional-params* , or positional-params* & next-param positional-param => binding-form next-param => binding-form name => symbol Defines a function"

17:28 OtherRaven: or at least it was too hard for me when I tried to read it XD

17:28 justin_smith: (source fn)

17:28 you can learn a lot just by calling source on various things

17:29 TheLastProject: That's an actual clojure command?

17:29 justin_smith: ,(doc doc)

17:29 clojurebot: "([name]); Prints documentation for a var or special form given its name"

17:29 bitemyapp: ,(doc source)

17:29 clojurebot: "([n]); Prints the source code for the given symbol, if it can find it. This requires that the symbol resolve to a Var defined in a namespace for which the .clj is in the classpath. Example: (source filter)"

17:29 OtherRaven: function, yeah

17:29 bitemyapp: ,(source doc)

17:29 clojurebot: Source not found\n

17:29 bitemyapp: alright fine.

17:29 TheLastProject: Heh, \n

17:29 amalloy: ~def doc

17:29 that's clojurebot-specific, though

17:30 justin_smith: in about fifteen minutes clojurebot will respond with "who's there?"

17:31 amalloy: heh

17:32 TheLastProject: DWIM - do what I mean

17:33 grind?

17:37 did I say 15 minutes? guess it is more like 7

17:38 TheLastProject: yeah, doc

17:38 in your own repl, doc and source will work

17:39 * TheLastProject nods

17:39 justin_smith: they are often as much manual as you need

17:39 amalloy: doc and source are standard

17:39 ?

17:39 but ~def, yeah

17:51 an eloquent insight on learning to program: http://i.imgur.com/cBComRu.png

17:53 * TheLastProject crawls into bed, way late

18:04 bitemyapp: justin_smith: perfect.

18:32 dotemacs: Hi, trying out liberator and I'm using the example the project has in it's documentation: (the 2nd example on this page: http://clojure-liberator.github.io/liberator/tutorial/decision-graph.html) but when I run it, the parameter is not being picked up, so I created this example which spits out the whole request: https://gist.github.com/dotemacs/7405304

18:33 the problem i'm seeing is that query-string within the request has the 'word=tiger' yet, the params within the request is blank

18:34 what am I doing wrong here?

18:35 danielszmulewicz: Me says you need the wrap-params middleware

18:35 :-)

18:37 dotemacs: http://www.refheap.com/20698

18:43 change defroutes app to defroutes routes

18:44 then define app like in the refheap

19:12 bitemyapp: arrdem: where did the pipelining stuff come from?

19:21 Morgawr: trivial question but.. in an agent if I provide error mode to be :continue it means that upon an error it will keep the same state as if the action on that agent had never happened, right?

19:22 so I can always be sure that sending stuff to that agent will never leave it in FAILED mode

19:23 bbloom: Morgawr: should be trivial to test that

19:25 Morgawr: bbloom: you're right, I was just wondering about irky situations like IO operations

19:25 but I can test it

19:26 mmm, IO is still executed but when the exception is encountered it's just ignored and the entity's state it kept intact

19:26 okay, that's a good explanation for me :P

19:27 bitemyapp: Morgawr: I just got done figuring a way to handle agent error modes and finalization

19:28 Morgawr: in Revise, specifically.

19:28 Morgawr: bitemyapp: do tell :)

19:30 bitemyapp: Morgawr: well in this case I made a somewhat unusual connection management strategy for a database client library.

19:30 Morgawr: agents were serializing access to the IO resource, in this case a socket and its associate in and output streams.

19:31 Morgawr: sockets can die, lose connection, yadda yadda.

19:31 in this case, it's better to take the Erlang approach, just let it break, and finalize the agent with error delivery to all "on deck" promises, basically telling the dependent callers to try again with a new agent.

19:31 Morgawr: interesting, yeah

19:31 bitemyapp: there's no point mutating the agent with a new socket in an attempt to recover, callers need to decide what's appropriate.

19:32 Morgawr: how do you notify the caller of the agent?

19:32 bitemyapp: in fact, attempting to do so could just cause cascading failure in situations like when a connection is refused or shutdown because the database is overloaded.

19:32 Morgawr: promises.

19:32 all query results happen through promises. There's an independent (as in, doesn't happen in the agent) reader thread for the socket too.

19:33 Morgawr: ah yeah

19:33 makes sense

19:33 bitemyapp: Morgawr: for more, see: https://github.com/bitemyapp/revise/

19:34 Morgawr: bitemyapp: I'll give it a look, thanks

19:48 bitemyapp: ddellacosta: welcome.

19:49 ddellacosta: morning

19:52 bitemyapp: ddellacosta: dunno if you saw, but I ended up coming up with a satisfactory solution to the error handling thing last night.

19:54 Morgawr: mm... I'm trying to use a "PrintWriter" from java (java.io.PrintWriter) but I can't quite get it to work with clojure

19:54 I try to do java.io/PrintWriter

19:54 but it says it can't find the class

19:54 isn't that how it should work? or do I have to import it too?

19:54 arrdem: bitemyapp: in what sense?

19:55 bitemyapp: as in where did I learn that stuff, or as in what's the reason for it?

19:55 bitemyapp: or just why am I writing about it?

19:56 amalloy: Morgawr: the / is for separating classname from method

19:56 the classname is just java.io.PrintWriter

19:56 Morgawr: amalloy: ah.. I see

19:56 thanks

19:58 ddellacosta: bitemyapp: sorry, I did see it but got busy at the very end of things and didn't respond. :-(

19:59 bitemyapp: although re-reading it now, I wanted to know why you said it "demanded a monad" to paraphrase. I didn't quite get it.

20:06 bitemyapp: arrdem: I'm just curious about it in general.

20:06 arrdem: also, up for a game?

20:07 muhoo: monads can be demanding. climbing up on your bed and pawingnat you because they want to be fed

20:07 arrdem: bitemyapp: I think I can get a box... lemme check

20:07 brehaut: muhoo: lolwat!

20:08 muhoo: brehaut: ddellacosta wanted to know why something demanded a monad

20:08 damn monads, always making demands

20:09 ddellacosta: damn you, monads

20:09 there ya go

20:09 arrdem: I shoulda used monads in that pipeline post...

20:09 the vm state is really a monadic value...

20:09 muhoo: monads always sounds to me like a body part you wouldn't want injured

20:10 brehaut: if injured badly enough, you may be left with only a monoid

20:10 bitemyapp: arrdem: it is indeed.

20:13 ddellacosta: I've acquired some materials on category theory, trying to form a more thorough understanding of how it all fits together.

20:14 brehaut: i was reading a basic category theory book yesterday. first three examples of categories were straight forward: set (got it), partially ordered set (got it), monoid (interesting, got it) and then Bam! a category for any abstract algebra (definately havent got it)

20:14 welcome to page 4

20:15 bitemyapp: saw an ordering (order by this, then that, then this) abstraction defined in terms of a Monoid. really tickled me.

20:15 brehaut: hah yeah

20:15 bitemyapp: brehaut: what book was this? the one I'm reading isn't bad so far.

20:16 brehaut: Basic Category Theory for Computer Scientists

20:16 bitemyapp: I'm reading "A Gentle Introduction to Category Theory - The Calculational Approach"

20:16 they're a bit vague about morphisms (I'm on page 8), but things are going okay so far.

20:16 brehaut: i think morphisms are just anything that changes a category structures shape

20:17 each morphism is quite distinct

20:17 i found homomorphisms (on monoids anyway) to be relatively grokable and interesting

20:18 basically (mappend (H a) (H b)) === (H (mappend a b)) (where H is a homomorphism)

20:18 s/H is a/H is any/

20:18 mtp: that smells like the distributive law

20:19 * arrdem starts putting together an amazon order

20:19 brehaut: mtp it might be, but its specifically defined in the context of categories

20:19 arrdem: bitemyapp: the gaming box is currently being used for stupid shit. I'll let you know if I manage to get an exclusive lock.

20:20 brehaut: or monoids at least

20:20 bitemyapp: arrdem: mutex lol

20:20 brehaut: so there are many Monoids around numbers, and around strings or lists

20:20 bitemyapp: anything concatenative or progressively accumulated, yeah.

20:20 sritchie: brehaut: fun time to come in :)

20:20 brehaut: yeah

20:20 sritchie: haha :) im reaching the end of what ive learnt

20:21 bitemyapp: interesting from a distributed systems perspective is to what degree you can formalize "independence" between operations performed.

20:21 once you abstract and "de-power" away from full turing completeness for the semantics of your operations, the closer you get to something robust, understandable, and scalable.

20:21 sritchie: I was playing around with simple-check and the monoid laws the other day -

20:21 https://gist.github.com/sritchie/7406280

20:21 brehaut: yes exactly. and you can trivially rewrite any expression between monoidal structures via homomorphism to order as your resources allow

20:21 bitemyapp: imagine if every query was a black-box program that mutates the state of the database?

20:22 instead of something declarative.

20:22 brehaut: one moment of minor confusion I've had is that this book defined fold as being commutative.

20:22 that was a bit ?_?

20:23 I guess that's true in some cases (reduce +), but it still threw me for a loop briefly.

20:23 brehaut: bitemyapp: i thing that if you define 'fold' as distinct from 'left fold' and 'right fold' then it may be true?

20:24 (isnt that what the reducers library does?)

20:24 bitemyapp: brehaut: I think so, yes.

20:24 amalloy: brehaut: yes

20:25 brehaut: sritchie: *brain asplode* ;)

20:25 sritchie: I want to encourage more monoids in Cascalog,

20:26 so users can write more general aggregations, then just extend the traits to their particular datastructures

20:26 brehaut: huh thats interesting

20:26 sritchie: making it trivial to move on from counts, to cardinality estimations, to moving averages, etc

20:27 Summingbird was all about encouraging users to use values with Monoids defined;

20:27 https://github.com/twitter/summingbird

20:28 as you were saying, we can get pretty far with systems optimizations knowing algebraic properties alone

20:28 associative - boom, we can add realtime and batch computed values together into one seamless, realtime looking value

20:28 commutative gets you more optimizations, like mapside aggregation; and so on.

20:30 coventry: Is there a version of clojure.walk which preserves all metadata? E.g.

20:30 ,(require '[clojure.walk :as wk])

20:30 clojurebot: nil

20:30 coventry: ,(->> [(with-meta (list 1) {:foo true})] (wk/postwalk identity) first meta)

20:30 clojurebot: nil

20:31 brehaut: sritchie: thats cool stuff

20:32 sritchie: im fascinated by the sorts of program transformation you can undertake when you level the really 'weak' algebraic structures (in contrast to say monad or arrow)

20:32 sritchie: yeah man, some of the most fun stuff to work on has been the graph optimization layer -

20:33 writing little optimizers that take the summingbird graph, tweak it, then pass it on to storm or scalding to get compiled down to a physical plan

20:34 amalloy: coventry: no

20:34 brehaut: sritchie: im curious if the programming world would have more excited about categories and algebras if they had heard about this stuff first, rather than mind benders like monads

20:35 sritchie: yeah, especially when you get into the whole "monad is a monoid in the category of endofunctors" set of one-liners

20:35 coventry: amalloy: Thanks. Any suggestions for a traversal framework which does? I am planning to try zippers next.

20:35 brehaut: sritchie: that one at least was intended as a joke i believe

20:35 sritchie: :)

20:35 brehaut: sritchie: but yes, it makes people blanch

20:35 sritchie: but no one knows it!

20:36 brehaut: sritchie: have you ever asked fsbot in emacs about monads and comonads?

20:36 in #emacs

20:36 sritchie: brehaut: have you played with any of the approximate data structures that have been coming out, like hyperloglog, count min sketch, etc?

20:36 haha, no

20:36 brehaut: its worth joining just to try it

20:36 amalloy: nothing i know of, coventry

20:37 sritchie: "[Too many DB matches] Accent on helpful side of your nature. Drain the moat."

20:37 :)

20:38 brehaut: haha

20:38 bitemyapp: brehaut: I just tried it, hahahahaha

20:38 sritchie: yeah brehaut is right, it's worth trying.

20:39 brehaut: the co- stuff in category theory made a lot more sense to me once i realised categories were all about topologies

20:39 (and that it just swaps the domain and codomain for the various arrows)

20:39 sritchie: brehaut: I remember asking you about monads a couple of years ago, and you taking the time to explain as well as you could via PM

20:40 my mind was blown - now, after the Scala years, it's all clear

20:40 but I haven't gone after comonads

20:40 brehaut: sritchie: haha cool. i vaguely recall now too.

20:40 ive looked at comonads from a distance

20:40 sritchie: I ended up writing a thrift parser using a parser monad:

20:40 http://github.com/sritchie/thrift-clj

20:40 bitemyapp: I can name a few examples of comonads but I don't really have a grip on it beyond a recitation of what it does at the category theoretic level.

20:41 sritchie: brehaut: a la your tutorial

20:41 brehaut: oh right. cool :)

20:41 amalloy: i've briefly come to an understanding of comonads, a few times

20:41 bitemyapp: amalloy: slippery.

20:41 brehaut: sritchie: im curious how the performance is

20:42 and im glad to see you switched to instaparse

20:42 sritchie: brehaut: I wanted to compare it to an instaparse version via mark engelberk

20:42 engelberg

20:42 now that I'm gone from Twitter working clj full time, it's more likely to happen

20:42 justin_smith: hey brehaut, you said you do web stuff with ring, right?

20:42 brehaut: justin_smith: yeah

20:42 bitemyapp: I think most Clojurians, when and if they do web stuff, do it in Ring :)

20:43 brehaut: one hopes ;)

20:43 sritchie: I've been playing with liberator, btw

20:43 I was asking this earlier -

20:43 though I'll stop if no one here knows liberator

20:43 it feels like there should be a built-in way to define base behaviors, then define resources off of merged maps of decision points

20:43 easy to write, of course

20:44 brehaut: so far ive only looked at liberator from the outside. havent had a need to use it in anger

20:44 justin_smith: brehaut: you may be interested, I made a ring middleware to store, retrieve, and replay ring requests https://github.com/noisesmith/groundhog

20:45 brehaut: haha i love the name

20:45 bitemyapp: justin_smith: that's pretty cool

20:46 now I want to watch that movie.

20:47 justin_smith: for the body, which might be binary, why not base64?

20:48 oh you did a byte-array anyway, n/m

20:51 john2x: what's the best way to replace all `x` values in a 2D grid with `y`?

20:52 arrdem: john2x: how's the grid represented?

20:55 justin_smith: heh

20:56 I left it kind of wide open in terms of storage if you want anything other than a global atom in the lib itself

20:56 eventually I may add some helper functions for db storage file storage etc.

20:56 but it's just edn so I figure people can roll their own

20:56 john2x: [[nil nil nil 0 nil][nil nil 1 nil nil]] and I want to replace the numbers with strings in an array ["foo" "bar"], using their index. [[nil nil nil "foo" nil][nil nil "bar" nil nil]]

20:57 justin_smith: bitemyapp: it is base64

20:57 with the reverse automatically being done on the way out

20:58 yeah, I wanted to keep strict method compatibility, so any middleware would get exactly the kind of data they expect

20:58 within reason

20:58 egosum: hmmm, anyone have any tips for nicely formatting JSON? I'd like to emulate something like this: http://jsonformatter.curiousconcept.com/

20:58 justin_smith: don't pass it a long arbitrary stream in the request body and expect anything sane :)

20:59 john2x: arrdem: [[nil nil nil 0 nil][nil nil 1 nil nil]] and I want to replace the numbers with strings in an array ["foo" "bar"], using their index. [[nil nil nil "foo" nil][nil nil "bar" nil nil]]

20:59 justin_smith: ie. if we read faster than you write, we assume you are done and we store what we have so far

21:01 arrdem: john2x: you'll need two mapv applications, identity, contains? and a map.

21:01 esehara: nick esehara

21:02 arrdem: bitemyapp: not getting a box tonight

21:02 justin_smith: john2x: I bet there is a concise way to do that with for

21:02 arrdem: justin_smith: not really unless you want to ... oh. yeah there probably is.

21:03 john2x: justin_smith: yeah, for and an atom to hold the mutating grid was my first thought. but was wondering if there was a more functional way.

21:04 arrdem: john2x: no you can definitely do this using only for. for is lazy. the for application you're thinking of won't work.

21:05 john2x: OT: hmm is it just my client or is arrdem's mentions of me sending doubles?

21:07 arrdem: john2x: ... wat

21:07 esehara: hi, Clojuren. I'm developer in Japan. I wish to translate official document to Japanese. Do you have good approach about it?

21:08 bitemyapp: arrdem: nuts.

21:08 brehaut: john2x: no its just you

21:08 john2x: no its just you

21:08 john2x: heh. I guess it's just me. Right, I keep on forgetting that for is lazy. How would I approach this for?

21:08 bitemyapp: john2x: no idea what you're talking about

21:08 john2x: no idea what you're talking about

21:08 john2x: ah, brehaut's mentions are in doubles too

21:08 everyone's!

21:08 brehaut: we are messing with you

21:08 * bitemyapp laughs and chokes to death

21:09 llasram: Plan succeeded?

21:09 * bitemyapp leaves to get chips and salsa

21:09 john2x: lol

21:10 brehaut: apparently im too honest to gaslight

21:10 egosum: (bump one more time) Does anyone know of any libraries for printing JSON nicely for e.g. APIs (formatted like something like this: http://cl.ly/image/2Z0o3y1N3d3u)

21:10 justin_smith: john2x: ((fn [grid] (for [x grid] (for [y x] (get {0 "foo" 1 "bar"} y y)))) [[nil nil nil 0 nil][nil nil 1 nil nil]])

21:10 arrdem: john2x: ok I lied. you need two fors.

21:10 john2x: but it is doable with only for.

21:11 justin_smith: ,((fn [grid] (for [x grid] (for [y x] (get {0 "foo" 1 "bar"} y y)))) [[nil nil nil 0 nil][nil nil 1 nil nil]])

21:11 clojurebot: ((nil nil nil "foo" nil) (nil nil "bar" nil nil))

21:11 amalloy: justin_smith: ##(doc replace)

21:11 lazybot: ⇒ "([smap coll]); Given a map of replacement pairs and a vector/collection, returns a vector/seq with any elements = a key in smap replaced with the corresponding val in smap"

21:11 egosum: Oh my, Chesire does that… https://github.com/dakrone/cheshire

21:11 arrdem: john2x: https://www.refheap.com/20707

21:12 amalloy: damnit

21:12 justin_smith: john2x: clojurebot takes forever to respond to me, but if you try that in a repl you will see it gives the output you specify

21:12 also ignores input not in the mapping

21:12 (passes identity)

21:13 arrdem: ?

21:13 amalloy: weird that replace is special-cased for vectors to return another vector

21:13 rather than just returning a seq in all cases

21:13 justin_smith: john2x: yeah, the version I posted aboce is pure functional

21:13 for is not imperative in clojure

21:14 the coincidence that it has the same name as the default imperative mutate a collection construction is too bad

21:15 amalloy: justin_smith: do you have a really bad internet connection, or do you just have a weird plan for reading the scrollback? you keep falling silent for several minutes, and then writing a half dozen messages at once as if you were in some past conversation

21:15 clojurebot's slow responses to you support the first theory

21:15 justin_smith: arrdem: I actually did this already using nested for, clojurebot will eventually spew out the correct results. He has a lag on my input for whatever reason.

21:16 just you

21:17 amalloy: yeah, definitely bad internet connection. clojurebot responded to you in one second

21:18 arrdem: justin_smith: oh. right. I saw that you posted a solution but I didn't really read it.

21:18 justin_smith: you have bested me, brb falling on my perentheses

21:19 justin_smith: john2x: you would approach it in exactly the way I pasted above, it is working code that produces the output you asked for

21:21 amalloy: &(map (partial replace ["foo" "bar"]) [[nil nil nil 0 nil][nil nil 1 nil nil]])

21:21 lazybot: ⇒ ([nil nil nil "foo" nil] [nil nil "bar" nil nil])

21:21 amalloy: is rather nicer imo

21:23 justin_smith: amalloy: good point

21:24 handy

21:26 amalloy: ok, something is messed up with my internet clearly

21:26 that would explain the lag of clojurebot's responses

21:26 and people acting like they haven't heard me

21:28 thanks for helping me figure that out, it was kind of gaslighting me :)

21:29 arrdem: lol

21:30 john2x: awesome. thanks guys. I'm really finding it hard to think differently about `for`.

21:31 arrdem: john2x: the critical thing is that for isn't the classical for.. it's a list comprehension. we have (doseq) for what you typically think of as (for). honestly (foreach) would be a better name than for...

21:34 john2x: yeah, but even then, there's no way I could've thought about using map, partial, replace to achieve what I wanted.

21:34 hyPiRion: It's about training

21:34 amalloy: practice, practice, practice

21:35 arrdem: i don't think foreach is a meaningfully different name

21:36 john2x: yeah. it's gonna take a *lot* of effort to make the paradigm shift. I've just recently gotten the hang of working with immutable data (and I love it).

21:37 arrdem: amalloy: once you bring the :when/:while option in it really isn't. I was just thinking about the specific case of a sequential "update" as here.

21:41 justin_smith: john2x: I wish they had picked a different name for 'for'; like maybe 'cartesian'

21:43 ,(for [x (range 4) y (range 4)] [x y]) ; cartesian

21:43 clojurebot: ([0 0] [0 1] [0 2] [0 3] [1 0] ...)

21:44 hyPiRion: 'for' is confusing because it's used differently in imperative languages

21:46 There is one function though... `contains?` anyone?

21:46 seangrove: contains?

21:46 clojurebot: contains? checks whether an indexed collection (set, map, vector) contains an object as a key. to search a sequence for a particular object, use the `some` function, which accepts a predicate. if you want to only match a certain object, you can use a set as the predicate: for example, (some #{5} (range)) finds the first occurrence of 5

21:47 seangrove: Love me some `contains?`, find myself reaching for it much sooner these days

21:48 hyPiRion: oh, humm...

21:49 ,(let [a (doto (java.util.ArrayList.) (.add 10)) b (java.util.ArrayList.) coll #{a b}] (.add b 10) coll)

21:49 clojurebot: #{[10] [10]}

21:50 hyPiRion: obviously, don't do that.

21:54 bitemyapp: I use contains? whenever the query I am performing makes sense in an associative context

21:54 arrdem: wait, so can you play LoL without borrowing a box, or was that for general purposes?

21:55 hyPiRion: yeah, I only use it on maps and sets

21:56 bitemyapp: hyPiRion: associative by key, and associative by value. I find the associative-by-index semantics of vectors a bit discomfiting when you start using stuff better suited to maps/sets

21:58 arrdem: bitemyapp: that was for general purposes. I haven't tried running sc2 or lol under Arch but it's worth a shot.

22:02 justin_smith: hyPiRion: woah, that is, like, spooky

22:03 bitemyapp: hyPiRion: why does the ArrayList look like a vector?

22:03 hrm, so you can mutate references inside of immutable collections. Yeah, that's not a fantastic idea.

22:04 hyPiRion: justin_smith: not really. It's just that maps cannot update themselves (and shouldn't) when "values" update

22:05 bitemyapp: not sensible for mutable collections either!

22:42 dobry-den: Seesaw is amazing

22:46 bitemyapp: ucb: http://www.youtube.com/watch?v=9vL9zCFpv-0

22:46 arrdem: bitemyapp: sc2 under Arch was the failz

22:46 bitemyapp: ucb: sorry if I've already sent it to you, I thought of it and figured it was good for you.

22:46 arrdem: attempted Wine I take it?

22:46 arrdem: bitemyapp: with unfortunate results.. part of ~/ got rm'd.

22:47 bitemyapp: :|

22:47 dafuq kinda amateur hour...

22:48 coventry: How does that happen?

22:49 * arrdem failed at rm'ing ~/.wine

22:50 arrdem: nothing lost tho... ~/config bit it, but that's under version control. restoring now... everything else that got hit is symlinks so no problem.

22:53 coventry: Oh, good. It'd really suck if an application misbehaved that way.

22:55 TimMc: arrdem: `rm -r ~/ .wine`?

22:55 arrdem: TimMc: something like that, yeah.

22:55 TimMc: Ouch.

22:55 * TimMc runs a backup

22:56 arrdem: TimMc: C-c saved the day, and git more crontab'd backups are making everything right.

22:56 I'm just suffering from amature hour as bitemyapp was kind enough to remind me.

22:56 hyPiRion: I'll give you guys a great trick I've learnt

22:56 coventry: I've made the same mistake, although a long time ago. I generally do an ls first, then back in the rm -rf.

22:56 TimMc: May I recommend installing trash-put and aliasing rm to it?

22:57 bitemyapp: arrdem: amateur*

22:57 arrdem: bitemyapp: go away

22:57 * bitemyapp stays put

22:57 TimMc: It was a dick thing to say.

22:57 bitemyapp: TimMc: I was actually assigning the blame to Wine.

22:57 hyPiRion: I have a file named '--foo' in my home directory, so if I ever do 'rm -rf *', I'll get an error message about a bad flag instead

22:57 bitemyapp: TimMc: it was his confession that made me aware the rm'ing of his ~ was his fault.

22:58 coventry: "rm bitemyapp" "rm: cannot remove `bitemyapp': Permission denied"

22:58 TimMc: bitemyapp: Ah, that's an important distinction.

22:58 You're thinking of e.g. the bumblebee incident?

22:59 hyPiRion: Nice.

23:00 technomancy: justin_smith: looks like you forgot to license groundhog?

23:00 also :source-paths in project.clj is redundant

23:00 TimMc: ls: unrecognized option '--false-flag'

23:01 bitemyapp: https://github.com/MrMEEE/bumblebee-Old-and-abbandoned/issues/123

23:03 Or rather https://github.com/MrMEEE/bumblebee-Old-and-abbandoned/commit/a047be85247755cdbe0acce6#diff-1

23:05 bitemyapp: TimMc: sigh, troll threads on github.

23:05 justin_smith: wasn't it npm that started removing people's directories for one of its updates?

23:05 bitemyapp: justin_smith: to be fair, disabling the computers of node.js users is a service to mankind.


23:06 justin_smith: https://github.com/isaacs/npm/issues/2293 maybe is what I was thinking of?

23:07 bitemyapp: "and I enjoy reinstalling things :)" <--- stockholm syndrome

23:08 justin_smith: ,'HELLO

23:08 clojurebot: HELLO

23:08 justin_smith: ok, it was emacs' fault

23:37 muhoo: sure, blame emacs

23:37 justin_smith: well, the restart fixed the issue, so..

23:42 muhoo: TimMc: holy shit. github->4chan

Logging service provided by n01se.net