#clojure logs

2013-11-10

00:00bitemyappalgal: don't.
00:00bitemyappalgal: either your SQL database is your point of truth or it isn't, don't try to be clever.
00:00algalbitemyapp: okay. what should I do instead? (honest question.)
00:00bbloomalgal: what database are you using?
00:00algalpostgresql
00:00bitemyappalgal: what sort of invariants are you talking about?
00:00bitemyappalgal: just write simple code.
00:01bbloomok, so an actual database :-)
00:01algalI've written simple code, but I'm not experienced with multithreaded programming at all so I am not confident it is correct.
00:01bbloomalgal: here's a good place to start with databases: http://www.postgresql.org/docs/9.1/static/transaction-iso.html
00:01bitemyappalgal: don't try to move consistency outside of the database.
00:02bitemyappalgal: your database should be resolving all conflicts and handling all transactions.
00:02bitemyappalgal: in brief, don't try to be clever. it'll bite you no matter what language you use.
00:02algalHere's an invariant: I have a database table that has columns USERNAME and SESSIONID, where SESSIONID is a UUID.
00:03algalI want to ensure that every username has 3 or less sessionIDs.
00:03bbloomalgal: 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
00:04bitemyappalgal: that has to happen in the database.
00:04bitemyappalgal: what bbloom said precisely.
00:04bbloomalgal: alternatively, you can do something simpler: allow them to go over 3 sessions, but afterwards, kill the oldest N-3 sessions
00:05algalbbloom: Thanks, this is very helpful. When you say "wrap your writes in transactions", you mean all within postgresql right?
00:05algalbbloom: (not STM transactions)
00:05bbloomalgal: right. if you issue multiple mutations from your client, you want to wrap them in a database transaction
00:06bbloomthat's not the default in rails, for example, and people get all sorts of stupid bugs b/c of it
00:06bitemyappbbloom: gotta win those benchmarks yo.
00:06bbloomthings 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
00:06algalbbloom: 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?
00:07bbloomalgal: you fetch somebody's session on every request right?
00:07bbloomwhen you say SELECT * FROM sessions WHERE id = ?;
00:07bbloomthen you only get one
00:07bbloombut you can do:
00:08bbloomINNER JOIN sessions AS self ON sessions.user_id = self.user_id
00:08bbloomthat will give you all of the sessions for each user. if it's greater than 3, you can issue a query to clear it
00:08bbloomthen there is no cron process
00:08bbloomif multiple threads all try to drop all older than 3, then the behavior is *idempotent*
00:09bbloomthere is no need for a coordinated read/write pair to prevent going over 3
00:09bbloomif you want to reeeaaally prevent going over 3, you need a stored procedure
00:09bbloomyour very first stored procedure has a very high complexity cost, b/c now you are versioning your code INSIDE your database
00:09bbloomif you can get away with no stored procedures, that's great, but realistically you'll need some in a large app
00:10algalbbloom: Wow, this is turning out to be just as complicated as I feared. I though this was an amazingly simple requirement..
00:10algalBut I am quite eager to understand this fully and rigorously.
00:10algalWould you say the postgresql page is a good place to start?
00:10bbloompostgres' manuals are excellent
00:11bbloomanother approach is to implement a trigger
00:11bbloomthe problem is that you don't want "fully serialized" transaction isolation
00:12bbloomit will kill your performance
00:12bbloomso it's always best to come up with designs that work if they fail in the middle
00:12algalbbloom: 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?
00:13bbloomalgal: clojure's concurency primitives are for *threads*, but you're going to have *clients*
00:13bbloomyou need consistency between machines
00:13bbloomyour database can provide that
00:14bbloomthat's not to say they aren't useful for other things
00:14algalBut in a servlet (this is a ring app), isn't the issue that separate clients could be represented by separate threads?
00:14bbloomright, but you can treat separate threads the same was as you'd treat separate boxes
00:15bbloomyou can think of it ass an optimization to start sharing memory & communicating between threads
00:15bbloomfor example, if you cached some stuff in memory, an atom would be a great place to store that
00:16algalokay.
00:16bbloombitemyapp: rails doesn't win any benchmarks ever. so i'll just s/rails/mongo/
00:17bbloomi haven't read it myself, but i hear great things about http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601
00:18bitemyappbbloom: no, no no
00:18bitemyappbbloom: don't recommend JCIP
00:18algalbbloom: Yes, I've been meaning to read that one.
00:18bbloomok, nevermind then
00:18bitemyappno
00:18bbloomlol
00:18bitemyappdon't.
00:18bitemyappalgal: don't.
00:18bitemyappalgal: save yourself.
00:18algalbitemyapp: why not??
00:18lazybotalgal: Uh, no. Why would you even ask?
00:18xuserisn't his underlying http server (jetty or http-kit) making his ring app concurrent already? without doing any manual concurrency in the clojure code
00:18bbloommy understanding was that it's a good intro to threading in general
00:18bbloomxuser: yes
00:18bbloomalgal: basically, for the typical app, all you need to do:
00:19bitemyappalgal: it's a terrible, terrible book.
00:19bitemyappI'm *still* angry I bought it.
00:19bbloomalgal: 1) be *aware* that multiple reads involve multiple network round trips, and other stuff might happen in between reads
00:20algalbbloom: Yes, this awareness is exactly what's got me puzzled about how to do everything correctly.
00:20bbloomalgal: 2) when doing multiple writes, subsequent writes may fail, and so you need a transaction to make them operate as a unit
00:20bbloomalgal: when in doubt, re-read http://www.postgresql.org/docs/9.1/static/transaction-iso.html a few times
00:20bbloomalgal: "Read committed" is the default and almost 100% definitely what you want unless you really know better
00:21bbloomalgal: 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
00:22bbloomalgal: and watch Rich Hickey's "are we there yet" video for an explaination of perception :-)
00:22bbloomthat's about it
00:22bbloomif you do that, your app will be 100X better than every rails app ever
00:22bbloomyou've made an excellent choice to use an actual database
00:22bbloomdon't use an ORM
00:23algalbbloom: 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.
00:23algalBut I was wondering if Korma would be a better way to handle it.
00:23xuseralgal: korma is just a dsl I think, sugar
00:23bitemyappKorma ain't an ORM. It's a DSL for SQL.
00:23algalok.
00:24bbloomehh... there are some ORM-ish things in there
00:24bbloombut it's certainly no active record
00:24xuseralgal: and it probably uses clojure.java.jdbc under the hood
00:24algalYou 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.
00:25bbloomalgal: 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/
00:26algalthanks, will have a look right now.
00:27swarthybbloom: reading this, love the title on your site
00:27bbloomswarthy: thanks!
00:28bitemyappbbloom: yeah I like that too.
00:29bbloomI 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
00:31algalbbloom: 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.
00:32bbloomalgal: 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...
00:32clojurebotc'est bon!
00:33bbloomhttp://www.amazon.com/Clojure-Bookshelf/lm/R3LG3ZBZS4GCTH
00:33algalgood thought
00:34bbloomif i ever move someplace w/ a few extra hundred square feet, i may buy some of those books :-P
00:34algala hundred spare months is the real bottleneck, I reckon.
00:35bbloomwho said anything about reading them?
00:35algal:)
00:37swarthybbloom: 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!
00:38bbloomswarthy: nice! i'm a nameless celeb!
00:38bbloomi'm pretty sure atwood owes me royalties, but i'll let it slide :-P
00:38swarthyahah
00:44bbloomalgal: so is your app a candidate for spit & slurp? :_)
00:44swarthybbloom: 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?
00:45bbloomswarthy: sadly not much
00:46algaloh, 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).
00:46algalAlso, I'm deploying on EC2 and I have the impression that EC2 instance's filesystems are not to be relied on for durability.
00:46swarthycool. Had you already built it with other stuff?
00:46bbloomwell, at least your client has good taste :-P
00:46algalbbloom: :)
00:47muhoodidn't chris write something called simpledb that is basucally spit/slurp an atom as edn?
00:47bbloomsomebody did
00:48algalSimpleDB is the name of one of Amazon's nosql services. or is this a different one?
00:48bbloomdifferent
00:48bbloomi think it was alandipert
00:48allenj12hey if i have a list of items i want to remove from another list, how would i do that?
00:49bbloomhttps://github.com/alandipert/enduro
00:49bbloomallenj12: how large are your lists?
00:49justin_smithallenj12: (remove (set unwanted) all)
00:49bbloomyeah, what justin_smith said, for most use cases
00:49justin_smith(that is if you want to remove all instances of specific values)
00:49bbloomunless nil or false are valid values in your lists
00:49lpvbtechnomancy: can I start lein up and then issue commands so I don't have to wait for the jvm to start up?
00:49algalbbloom: Oh hello, this endure choppy can use postgresql as a backing store....
00:50allenj12ahhh kk i thought that only worked for single cases
00:50allenj12ty
00:50bbloomalgal: eh. if you're going to use postgres, use postgres
00:50justin_smithallenj12: 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
00:51allenj12ooooo kk
00:51justin_smithallenj12: if nil or false are valid values that you do not want to remove, you could use contains? (remove (partial contains? (set unwanted)) all)
00:51algalbbloom: yeah, it's probably just write text into a field..
00:53allenj12justin_smith; no the first will do but that is good to know ty very much
00:54algalbbloom: great post, btw..
00:54bbloomalgal: thanks
00:55algalI totally hear you w/r/t/ to rediscovering simplicity ten years later. :)
00:55bbloomoh yeah :-)
00:55bbloomevery good dev has their OOP class hierarchy days, and their IT MUST SCALE TO A BAJILION days
00:56bbloomand a few other phases w/ perfect coding standard and whatnot
00:56bbloombut 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.
00:56algalbbloom: Well, we grow up with what's in the air.
00:57algalbbloom: And late 90s was full of mis-emphasis on large scale and class tangles.
00:57algalanyway, that's I how remember it. :)
00:57bbloomalgal: i recently saw some C# code i wrote while at msft. i was like "did i really document my functions like that?"
00:58john2xi'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)
00:58bitemyappmy code 6 months ago was terrible.
00:58bbloomjohn2x: for is lazy
00:58bbloomjohn2x: you should not have side effects inside lazy operations
00:59algalmy code 6 days ago did not think through data consistency. so we you never stop learning. maybe because I never stop forgetting. :|
00:59bbloomjohn2x: move your swap outside the for
00:59bbloomyou can do something like: (swap! word-syms assoc (for ... ))
00:59bbloomor rather:
00:59bbloom(swap! word-syms assoc (concat (for ... ))) where your for returns [key value] pairs
01:00bbloomer dur, with an apply!
01:00amalloybbloom: (apply assoc! ...)
01:00amalloyer, apply swap!
01:00bbloom(apply swap! word-syms assoc (concat (for ...)))
01:00amalloyalthough i don't know why you'd use apply assoc rather than into
01:00bbloomamalloy: lol duuuh
01:00bbloom(swap! word-syms into (concat (for ...)))
01:01bbloomor no concat
01:01amalloybbloom: (= (concat x) x)
01:01bbloomignore me
01:01bbloomi'm totally dumb right now
01:01bbloomsorry, i failed miserably. that means it is way past bed time
01:01bbloomjohn2x: listen to amalloy. he seems to be awake
01:01john2xthank you both :)
01:01bitemyappddellacosta: I figured out my problem. I forgot how POSIX works and now realize I have to just nuke all waiting promises whenever anything fails.
01:01bitemyappaka, the nuclear option.
01:02ddellacostabitemyapp: d'oh.
01:02bitemyappThis 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.
01:03bitemyappddellacosta: 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.
01:03bitemyappthe async nature of my implementation means "one or more" writers will get an exception.
01:04bitemyappinterestingly, because of the constant reader thread, a user could actually notice the agent was killed off before even attempting the write if they wanted.
01:04bitemyappso in actuality, its "0 or more" writes will fail.
01:04bitemyappwhich is interesting.
01:04bitemyappthis error scenarios beg for a goddamn monad.
01:20algalbbloom: 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.
01:20bitemyappthere are a lot of people here happily doing Clojure web dev.
01:21bitemyappyou might ask if any of them have a setup that works for them instead of seeking a negative result.
01:21algalbitemyapp: what are most of them using, is my question. Just building directly on top of ring/compojure? or ... ?
01:21bitemyappalgal: ring/compojure is the most popular. a good "getting started" setup for that is Luminus. http://www.luminusweb.net/
01:27algalbitemyapp: 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.
01:27algalBut I can see how it's a sensitive topic so maybe I'll just shut up. :)
01:28bitemyappalgal: it's not a sensitive topic, just trying to provide methodological guidance.
01:28bitemyappalgal: Clojure's current web stack lends itself very well to data heavy projects.
01:28bitemyappstuff 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.
01:30algalbitemyapp: 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?
01:32bitemyappalgal: both, anything where you've got a lot of cross-talk between server and client too.
01:32justin_smithalgal: I think data heave means large amount of data being summarized or operated on in some way
01:33bitemyappalgal: yes, what justin_smith said.
01:34algalok. thanks for the perspective.
01:38bitemyappsweet, my error handling works.
01:39justin_smithbitemyapp: nice, how did you work it out?
01:39bitemyapp勝!
01:40bitemyappjustin_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)
01:41bitemyappthe "run" function throws if the promise is an instance of java.lang.Exceptio
01:41bitemyappand I'm adding an "agent dead?" check as well.
01:41TEttingerbitemyapp, if I order that at a chinese restaurant, will i get kicked out?
01:41bitemyappthere's a possibility for a race condition here but I've got a timeout and a secondary check that I'm adding.
01:41justin_smithyou would think there would be something more concise for such a common thing
01:41bitemyappTEttinger: it's katsu, the japanese for victory. So...I don't know?
01:42bitemyappjustin_smith: hum. yeah there's no good way to do this, mostly because people use sockets in a synchronous way usually.
01:42TEttingerah, I saw many little strokes and thought chinese
01:42bitemyappjustin_smith: so its expected your synchronous reader/writer will have knowledge of direct causality.
01:42justin_smithTEttinger http://en.wiktionary.org/wiki/%E5%8B%9D
01:42bitemyappjustin_smith: the way I designed revise's conn mgmt uses sockets MUCH more efficiently but there's some non-determinism here.
01:42TEttingerI have had chicken katsu now that I think about it
01:43bitemyappTEttinger: that's a japanese dish though :)
01:43TEttingerindeed
01:43bitemyappthe 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.
01:44bitemyappI guess this comes with the territory of async + using socket resources in a semi-novel way.
01:44justin_smithTEttinger: found that via wiktionary, via a google search, seems it is victory in han and kanji
01:44TEttingerit looks like a lot of strokes
01:44justin_smithwe just need an immutable and atomic network layer
01:44justin_smithand these concerns are obviated
01:45algalbbloom: also liking this one on ORMs and declarative schemas.
01:46justin_smithTEttinger: 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
01:46TEttingerhaha
01:49bitemyapp`cbp: around?
01:53`cbpbitemyapp: kinda what's up
01:53`cbpbitemyapp: oh you got the error handling down? nice
01:54jared314when connecting datomic to a heroku postgres instance, how do you add the NonValidatingFactory?
01:54jared314i tried the db params but it still gives me a cert error
01:57bitemyapp`cbp: yeah it works, but there are more changes to the API
01:57bitemyapp`cbp: breaking ones, unless you object.
01:57bitemyapp`cbp: run is derefs promise with a timeout and checks for errors by default, run-async just returns the promise, no error handling.
01:58bitemyapp`cbp: ordinarily I wouldn't have fucked with run, because I know it's *everywhere*, but the safety really commends the change.
01:58`cbpso the previous run is now run-async?
01:58`cbpand run blocks?
01:58bitemyapp`cbp: yep and yep, but run blocks with a timeout that can be overridden.
01:58bitemyapp`cbp: want me to just push?
01:59bitemyappthis also means query results don't need to be deref'd anymore.
01:59`cbpbitemyapp: yeah just push its not very breaking
02:01bitemyapp`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.
02:01`cbpok
02:03bitemyapp`cbp: bja ended up getting hijacked by some important work-stuff, but possibly they can collaborate on a future project with us :)
02:06bitemyappsweet! my hypothesis was right.
02:07bitemyapp`cbp: pushed!
02:07bitemyapp`cbp: are you willing to fix the tests?
02:07bitemyappif not, I can get rid of the unnecessary derefs.
02:07`cbpits just 2 :p
02:07`cbpill be available later
02:08bitemyapp`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.
02:08bitemyapp`cbp: on the bright side, proper error handling now!
02:08bitemyappnoodling how to handle errors in an asynchronous connection model like this wasn't super-easy :)
02:09`cbpits pretty original
02:09`cbpfar as i know
02:11bitemyapp`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.
02:11bitemyapp`cbp: just pushed a last minute addition, reader thread kills itself off if there's an agent error.
02:12bitemyapp`cbp: also, I don't really know exactly why, but when the rethinkdb server goes down, the socket receives a bunch of {} responses.
02:12`cbphuh
02:12bitemyappI use that to detect socket death / server shutdown at the moment.
02:12`cbplol
02:12bitemyappspecifically, to make the errors more descriptive.
02:12bitemyappit handles any source of error I can fathom right now.
02:13bitemyappwhen you see the logic in (defn run ...) you'll see how paranoid I'm being.
02:13bitemyappyogthos: hi!
02:15bitemyapp`cbp: with that, I'm going to play some League of Legends. Ping me if you need anything.
02:18`cbpbitemyapp: should join us at dota sometime :)
02:21bitemyapp`cbp: I play DotA2, I'd love to play with you
02:21bitemyapp`cbp: ping me whenever you'd like to do a game
02:24bitemyapp`cbp: I mostly play league because the games finish faster than DotA2
02:24`cbpbitemyapp: i play with some friends and we used to lol but were bored of it now
02:24bitemyapp`cbp: well you know where to find me :)
03:10muhoowhat does this weird destructure form do? (let [{[foo] :bar} mystery] ..)
03:12muhooi've seen {:keys [foo] :as bar} many times, but not {[foo] :bar} before
03:13noidimuhoo, {local-name :key} takes the value under :key from the map, and assigns it to local-name. local-name may itself contain further destructurings
03:13noidiso, in your case if the map looks like {:bar [1 2 3]}, the value of foo will be 1
03:14noidibecause of the destructuring (let [[foo] [1 2 3] ...)
03:26weiwhy doesn't this try/catch block work? https://gist.github.com/yayitswei/7395401
03:27weioh-- nevermind, it actually does work!
03:55BlackBlockis there a lib for making partials in general ? (e.g. make partial fn with arbitrary arg positions)
03:58bitemyappBlackBlock: there's not, and you should avoid that. The general pattern you're seeking is currying.
03:59bitemyappBlackBlock: currying is something you should look into. core.reducers had a defcurried. Also, you probably want closures.
04:16brainproxystate monad (protocol-monad) + core.async + trampoline = neato
04:18danielszmulewiczIs ritz (the debugger) a dead project?
04:19bitemyappdanielszmulewicz: nope.
04:19danielszmulewiczbitemyapp: Has it been updated to work with cider?
04:21danielszmulewiczDid you guys already update from nrepl.el to cider?
04:22danielszmulewiczLast time I tried (on day 1 of the cider release) there were too many problems and I reverted back to nrepl.el.
04:22danielszmulewiczThings are stable now?
04:23ambrosebsdobry-den: byte array should be (Array Byte)
04:29bitemyappdanielszmulewicz: that's on Cider, not Ritz.
04:29bitemyappdanielszmulewicz: don't use cider if you aren't willing to fix things yourself.
04:31danielszmulewiczbitemyapp: are you using Ritz?
04:33bitemyappdanielszmulewicz: occasionally, yes.
04:34danielszmulewiczbitemyapp: in Emacs?
04:34bitemyappyes...
04:35danielszmulewiczare you wih nrepl.el or cider?
04:35bitemyappdanielszmulewicz: I'm going to bed momentarily, you have about 2 minutes to ask a useful question.
04:35danielszmulewiczbitemyapp: good night
04:35bitemyappnrepl.el, because I'm not dumb enough to use cider while it's broken as fuck.
04:35danielszmulewiczbitemyapp: that's what I'm asking
04:36danielszmulewiczbitemyapp: I've seen some enthusiastic praises for cider on the Clojure community on Google+, so I was wondering...
04:36bitemyappcider is just nrepl.el, but with all the integration broken because shit is half-renamed.
04:37bitemyappthere's nothing to praise.
04:37bitemyappI have no idea why you would take sycophantic comments on Google+ seriously.
04:37bitemyappinstead of actually looking at what was done
04:38danielszmulewiczbitemyapp: You made your point. You must be tired. Sleep well.
04:46bitemyapp~cider is Cider is unstable and broken right now, most things don't work. Use the last stable version of nrepl.el for now.
04:46clojurebotYou don't have to tell me twice.
04:46bitemyapp~cider
04:47clojurebotcider is rage-inducing
04:47bitemyapp~cider
04:47clojurebotcider is rage-inducing
04:47bitemyapp~cider
04:47clojurebotcider is Cider is unstable and broken right now, most things don't work. Use the last stable version of nrepl.el for now.
04:47bitemyappvery good.
04:47bitemyapp~cider
04:47clojurebotcider is rage-inducing
04:47bitemyapplol :)
06:19allenj12is there an easy way to go from a 2d list to a 1d list
06:23allenj12nvm got it (apply concat)
07:07rrcI 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?
07:15john2xcurious, is this fn in good form/idiomatic? http://vpaste.net/R8AH5 using an atom for mutation in a for loop.
07:17dobry-denjohn2x: no
07:18john2xhmm. what would be a better way to achieve what I want?
07:18dobry-denyeah sorry, i pressed enter too soon.
07:19dobry-denfirst of all, im pretty sure that :let (in the for-loop) is run every iteration
07:19dobry-denso that slurp is being run
07:20john2xyes, that part's intended. it's calling an api request for each `word`
07:20dobry-denoh i see
07:20dobry-denit's an http request
07:20dobry-denhaha
07:20john2xyes :) it's an api to a thesaurus
07:21dobry-denthe first thing youd want to do is make many requests at once
07:21dobry-denpretty sure that's gonna wait for the full slurp on eeeeevery word
07:23dobry-denContinuing on our naive path, an easy change would be to turn the atom into an agent
07:24dobry-denand use send-off in the loop
07:25dobry-denhttp://clojure.github.io/clojure/clojure.core-api.html#clojure.core/send-off
07:26john2xah, I kinda need the fn to be synchronous. :) it's basically to get synonyms of a list of words.
07:26dobry-dendoes each request have to be synchronous?
07:27dobry-denthe idea is that you make all the requests asynchronously, but synchronously wait for them all to complete
07:28john2xnope. ah, I think I follow now. so I'll just realize the agent and then start parsing the responses?
07:29dobry-denyeah. although still not an ideal solution, but it came to mind an incremental improvement
07:30dobry-denyou could probably even make a trivial change to use `pmap` and begin processing immediately, but it's more for cpu bound things
07:34dobry-denunfortunately youre not talking to the parallel programming guru here.
07:35john2xthanks. improvement is improvement :) what about the part of using an atom as a variable for keeping track of a mutating object while iterating/looping?
07:36dobry-denlike 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
07:36Morgawris 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
07:37dobry-denjohn2x: in general you want to use loop/recur and update the loop binding when it's sequential/synchronous stuff
07:38dobry-denbut of course here we'd wanna coordinate work across threads
07:38hyPiRionMorgawr: Probably same as why alter, commute, ref-set and ensure doesn't end with an '!': Different semantics within a transaction I guess.
07:38dobry-denjohn2x: also, iirc, the swapping an atom can actually run the function multiple times
07:39dobry-denit does something called compare-and-set
07:40dobry-densince you dont actually care what order the requests conjed to the ref, atom doesnt make sense
07:40MorgawrhyPiRion: but agents don't have to run inside a transaction though
07:40hyPiRionI know
07:40Morgawrbut yeah, just wanted to know if there was any explicit reasoning behind this or if it's just an occurrence
07:40dobry-dennow u got 'im shurgin
07:40hyPiRion*shrug, heh
07:40Morgawrlol
07:41dobry-denMorgawr: Only Hickey Knows
07:41hyPiRionIt's probably not too far off from what you said, dobry-den, something about no retries
07:42dobry-deni 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.
07:42dobry-denso every day was this constant game of trying to remember which functions had the bang
07:43hyPiRionhaha
07:43dobry-densort of like being stuck in a hell where you're trying to plug in usb drives the right way every time
07:44Morgawrdamn 4-dimensional usb sticks
07:45hyPiRionI wish they were something like a CAT cable with a little bump up on one side
07:45dobry-deni wish it was like, a triangle
07:45dobry-dener wait
07:45dobry-denwhy not just a circle
07:45hyPiRionyeah, hah
07:45MorgawrI'd just take it like a normal audio jack
07:45Morgawrjust plug it in and forget about it
07:45dobry-denthat's not how you make money
07:46Morgawris that why I'm poor? :(
07:46dobry-den:(
07:46john2xor PS/2. now that was hard to get right
07:46dobry-denhaha
07:46dobry-denon the dark side of a computer buried on the underside of a desk, yeah
07:50dobry-denjohn2x: i often use http://http-kit.org/client.html for http requests which makes async stuff easy
07:52john2xdobry-den: thanks for help. :) I'll finish the rest of my project first then go back to improving.
07:53dobry-denyeah that's a trait i would like to have
07:54dobry-denbut the yaks arent gonna shave themselves, i tell myself
07:55john2xand yet here I am asking about whether a fn was idiomatic with still a lot left to finish ;P
08:05dobry-denit would be nice if clojure had a batteries-included way to consume the first N elements of async jobs.
08:07dobry-denbasically, a pmap for i/o bound jobs
08:09dobry-denthe 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,
08:10dobry-denand, 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
08:11dobry-deni'm gonna write it right now ^_^
08:11dobry-denpooled-map
08:14dobry-dendont try and stop me cuz u cant
08:45opqdonutdobry-den: seque does almost that
08:46opqdonutdobry-den: We have a custom version of pmap that takes an ExecutorService (i.e. thread pool)
08:46opqdonut... which is useful
09:23CookedGr1phonHi, 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, ...]
09:24CookedGr1phonso what I really want to do is override the tostring, but I'm creating it from a static allocate method
09:24CookedGr1phonand it's all naff and convoluted
09:24CookedGr1phonis there any other way to override the str output to give me a useful dump instead?
09:24CookedGr1phonor am i stuck going down this route
09:24CookedGr1phon(hoping someone's going to tell me about a handy to-string protocol or something)
09:35CookedGr1phonwhat is it that appends the #<Classname ... > part of the output?
10:22noidiCookedGr1phon, you can't customize str output, but you can define a custom print-method http://clojuredocs.org/clojure_core/clojure.core/print-method
10:23noidiand then use pr-str to create a string representation of your buffers
10:26CookedGr1phongenius!!! Thankyou so much
10:26CookedGr1phonthat works perfectly
10:27CookedGr1phonexactly 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:29CookedGr1phonthis is infinitely more readable
10:31justin_smithwhat about print-dup?
10:32justin_smithI thought pr-str used print-dup
10:33justin_smithwith *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:35justin_smith(the chain of calls going down being pr-str -> pr -> clojure.core/pr-on
10:36justin_smith)
10:38CookedGr1phonwhat 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:39CookedGr1phonother than print-dup only does it when you tell it to use that representation specifically?
10:40justin_smithpr-str is meant to be readable
10:40justin_smitherr
10:41justin_smithprint dup is called by pr-str
10:41CookedGr1phonand print-dup is meant for serialisation?
10:41justin_smithif you set the *print-dup* special
10:41justin_smithotherwise it calls print-method
10:41justin_smiththe first is if you want something that is loadable from a string to get the same thing back
10:42justin_smiththe latter is meant to be readable
10:42CookedGr1phonokay, I only care about looking at this, i'm not trying to serialize, there's much better serializations for this data
10:42justin_smithcheck out (source clojure.core/pr-on), which pr-str ends up calling a couple of levels down
10:42justin_smithyeah, pr-str calls it if you set that special
10:43justin_smithbut if all you care about is readable output, setting print-method should suffice
10:44CookedGr1phoncool
10:44CookedGr1phonthanks for the clarification
11:06CoconutCrabfree
11:06CoconutCrabops, sorry, wrong window
11:14justin_smithhttps://www.refheap.com/20657 any hints why this doesn't print anything but a blank line?
11:15justin_smithupdated to show a call that should output but does not
11:16justin_smithNEVER MIND IT'S LAZY AND IM DUM
13:12sritchieany liberator users?
13:12sritchieI'm trying to return a 404 not found page for a resource,
13:12sritchiefor js, css or img
13:14`cbpbitemyapp: ok i added some docs and fixed the tests and deployed 0.0.4
13:16`cbpbitemyapp: let me know if you want me to add something else to the docs
13:17edoloughlinAnyone 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:17danielszmulewiczsritchie: liberator user here
13:18sritchieI think I just found my answer: liberator.representation/ring-response
13:18sritchiefor returning a different content type from, say, a js request
13:18danielszmulewiczsounds about right
13:18danielszmulewiczindeed
13:18sritchiethe other Q I had
13:19sritchiewas about how to stack behaviors
13:19sritchiesay I want to share authorization logic,
13:19sritchiebut have everything else be different
13:20danielszmulewiczliberator has :authorized? which you put everywhere where you need to authorize a resource
13:21danielszmulewiczDoes that address the question?
13:21sritchiewell, yeah -
13:21danielszmulewiczOK, cool
13:21sritchiebut is the pattern to make a function taht returns a resource?
13:21sritchie(defn get-resource [auth-fn] (resource :authorized? auth-fn ….))
13:22sritchiedanielszmulewicz: or is there a way to compose the two
13:22danielszmulewiczno, the pattern is to write a function to return a boolean
13:22danielszmulewicz*that*
13:23sritchiedanielszmulewicz: yeah, for authorization
13:23danielszmulewiczsritchie http://clojure-liberator.github.io/liberator/doc/decisions.html
13:24sritchiethe question is how to nest them
13:24sritchiefor example, say I have the same handle-not-found logic for every one of my resources
13:24sritchiesince these are all constructed like maps,
13:24sritchieit would make sense to be able to merge them, for example
13:24danielszmulewiczyou don't nest anything, that's the beauty of liberator
13:24danielszmulewiczyou have a decision graph
13:25sritchiebut you duplicate quite a bit if you have 10 resources with the same handle-not-found fn
13:25danielszmulewiczanywhere in the chain you can change the outcome of the request
13:25sritchieyup, got that part, it's really awesome
13:25sritchiewhat I want to do is lock down a bunch of those decision points,
13:25danielszmulewiczif you have
13:25sritchieand then write resources that fill in the rest
13:25sritchiesame auth, same :handle-not-found
13:26danielszmulewiczyou reuse the same function
13:26danielszmulewiczthat's not called duplication
13:27sritchiebut every resource has :handle-not-found handler
13:27sritchiewhen you could imagine something like
13:27sritchie(defresource my-resource :base-resource something-else :handle-ok ....)
13:27sritchielike, a base-resource key
13:28sritchiedanielszmulewicz: and then you'd just merge the two maps to create the final resource
13:28sritchieif you've got 10 decision points that you want to share for every resource,
13:28danielszmulewiczI think you can do something like that with the routing but I don't know exactly how
13:28sritchieI would argue that it's not clean to have to copy-paste those ten lines
13:28sritchiethose 10 key-function pairs
13:28sritchieokay, liberator routing?
13:29danielszmulewiczit's the compojure part where you can generalize routes
13:29amacdougallHere'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:29amacdougallAnyone know what's up?
13:29amacdougallI'm on ClojureScript 2030.
13:29sritchieoh, then internally start switching again, in handle-okay, using compojure?
13:29sritchieyeah, I guess you could use a resource as a middleware
13:29sritchieinteresting, that would work
13:29danielszmulewiczyes, liberator is ring compliant and I think you can sudy compojure addanced routing
13:29danielszmulewicz*study*
13:30sritchiesweet, I think that's the right call. thanks guys
13:30danielszmulewicz*advanced*
13:30sritchiethanks danielszmulewicz, that is :)
13:30danielszmulewiczsritchie: cool, good luck
13:32amacdougallI 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:40amacdougallHm, look like it might be fixed by this commit: https://github.com/clojure/clojurescript/commit/dbf31380b925815d832a6cc53364d063a59dafad
13:56rabidsnailWhat's the idiomatic way to do argmin?
13:57justin_smithrabidsnail: waht is argmin?
13:57justin_smithn/m wikid it
13:59rabidsnailI mean I could do it with loop/recur, but it seems like there should be a cleaner solution
14:00dobry-denI 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:00justin_smithrabidsnail: I am putting together a version with reduce, assuming args are a sequence to iterate
14:00justin_smithgenerating that sequence is a separate problem I think
14:01rabidsnailjustin_smith: yeah, I'd expect the signature to be (argmin function sequence)
14:02justin_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:02justin_smithmaybe
14:02justin_smiththat allows args to be more than one arg, just put each set of args in an array or vector
14:03rabidsnailright, that'd work
14:03rabidsnailI'm kind of surprised there's nothing like that built in
14:04rabidsnailit's a really common thing to want to do when translating math out of papers
14:04justin_smiththough I used the name "arg" one too many times
14:08justin_smithrabidsnail: https://www.refheap.com/20677
14:08justin_smithmy go at it
14:09justin_smithif the function is always single argument you can simplify it a bit by taking out the apply usage
14:10justin_smithalso the order of arguments should be reversed so the sequence is last
14:10justin_smiththat is the normal order for clojure
14:10CookedGr1phon(max-key (comp - f) a b c d ...)
14:11CookedGr1phonoh
14:11CookedGr1phonor min-key
14:11CookedGr1phonthat would also work
14:12justin_smithCookedGr1phon: awesome, I did not know about max-key
14:12CookedGr1phonit's a bit weirdly named
14:12justin_smithor min-key!
14:12justin_smithnice
14:12justin_smithTIL
14:13arrdemclojurebot: ping
14:13clojurebotPONG!
14:13justin_smithrabidsnail: so your assumption was correct, it did exist, and it had the most intuitive name :)
14:13justin_smith*second most intuitive
14:14rabidsnailCookedGr1phon: thanks
14:14CookedGr1phonnp
14:14justin_smithyeah, why think in terms of keys rather than functions?
14:15cYmenhm... 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:15justin_smithalso it runs f twice as often as my version does, looking at the source :)
14:15cYmenI'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:16justin_smithI like my version better, it calls the function half as many times and allows for multiple arguments to the function :P
14:17justin_smithcYmen: using ring, I usually fire up 'lein ring server' and start an nrepl from inside the ring process
14:18justin_smiththat 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:18justin_smiththe github page for nrepl shows how to start the server from inside your app, it is straightforward
14:20cYmenjustin_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:22justin_smithcYmen: as I said, it is mentioned on the github page: https://github.com/clojure/tools.nrepl
14:22justin_smith(defonce server (start-server :port 7888))
14:22cYmenuh yeah already copy pasting stuff
14:22justin_smithyou would run that inside the init function of your ring app
14:23cYmeninit function...
14:23cYmen*sigh*
14:23cYmenThere 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:24justin_smiththe init function being the one that is passed as :init to the :ring key in your project.clj
14:24justin_smithand that is where you define it if you don't have one already :)
14:24cYmenapparently I don't
14:25justin_smith(defproject ... :ring {:init your.ns/your-init-f})
14:25justin_smiththat's all you need
14:26justin_smithwell the stuff in the ... is assumed to already be in there :)
14:26cYmenYeah, I got that part. :p
14:26justin_smithcYmen: there is a bootstrapping period, after which the help all starts to make sense
14:26justin_smithin my experience
14:27justin_smithwhat I am saying is if you stick to it you will get there
14:27justin_smithand feel free to ask clarifying questions if my help isn't helpful
14:28justin_smithI can share a project.clj of mine if you want to see what I mean regarding how the :init fn is defined
14:28cYmenI should be able to get that right, let me check. ;)
14:30justin_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:30cYmenSo ....uh if I did it right how do I know?
14:33justin_smithit will open the port, you can connect to it with 'lein repl :connect <port>' in a terminal
14:33justin_smithif that works, next step is making fireplace connect to that port
14:34yedianyone know of langs where 0 is falsy and "" is not? (or vice versa)
14:34justin_smithso if you pasted that code, 'lein repl :connect 7888'
14:35xuseryedi: C ?
14:36cYmenjustin_smith: It keeps telling me "Port is required"'
14:37swarthycYmen: 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:38justin_smithxuser: I think "" is a NULL pointer in C, which is usually 0, which is falsey
14:38mtpjustin_smith‘ the empty string is not a null pointer
14:38justin_smiththe C spec says any value can be NULL for a specific platform, but 0 is normal
14:38justin_smithBUT
14:39justin_smithcYmen: lein repl :connect 7888
14:39justin_smithor whatever port number you told the server to open on
14:40cYmenjustin_smith: I think my irssi just crashed, sorry.
14:40cYmenjustin_smith: LEIN_REPL_PORT=7888 lein repl :connect works. See https://github.com/technomancy/leiningen/issues/1344
14:40amacdougallbja just helped me out big time with my ClojureScript issues -- thanks!
14:41justin_smithmtp: what about an empty string const literal?
14:42justin_smith that would be the value 0 no?
14:42mtpjustin_smith‘ still not a null pointer
14:42mtpthat gets allocated in rodata iirc
14:42justin_smiththough the address of that string is not likely to be 0, it will have 0 as its first value
14:42mtpexactly
14:43mtpthe name of the thing, is not the thing
14:43justin_smithwhich is the value you get if you deref that location
14:43mtpbut that still isn't a null pointer!
14:43mtpif you deref it, you get (char)0
14:44mtpa null pointer is (char *)0
14:44justin_smithcYmen: oh yeah, I have been avoiding upgrading my lein :)
14:44xuserjustin_smith: https://www.refheap.com/20680
14:45justin_smithmtp: implementation wise the first value you read when you read something that is an empty string will be a 0
14:46mtpjustin_smith‘ but according to the type system, that is still not a pointer to null
14:46mtpit is a null byte
14:46cYmenjustin_smith: So how do I connect to that using fireplace? I can't seem to do it with :Connect.
14:46justin_smithyeah, I was wrong about the NULL thing, that is also usually 0, but beside the point
14:46justin_smithyeah
14:47justin_smithindeed
14:48justin_smithmtp: as I said above "though its address won't be 0, its value when you deref will be"
14:48mtpjustin_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:48mtp(void *)0 is not ever guaranteed to be (char)0
14:49mtp(it *frequently is*, on today's machines)
14:50rabidsnailIs 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:50rabidsnaillike, is there a protocol for comparisons?
14:51justin_smithxuser: 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:51mtpimagine 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:51mtp:)
14:51justin_smithmtp: yeah, I said a couple times I was totally wrong about that null thing already
14:52mtptl;dr there are a lot of subtleties here
14:52justin_smithagreed
14:52mtphttp://thoughtmesh.net/publish/367.php
14:52justin_smithI only briefly implied otherwise, and have since corrected myself
14:53cYmenjustin_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:55justin_smithmtp I actually wouldn't be surprised if that existed somewhere
14:56mtpjustin_smith‘ yeah, it is perfectly legal C, but it breaks the assumption that (char)0 == (void *)0 :)
14:57mtp(upon which we now agree)
14:58justin_smithcYmen: 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:59justin_smithor, 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:00justin_smithmtp: fascinating article
15:01xuserjustin_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:03xusers/if/in/
15:05justin_smithxuser: true
15:05justin_smithit was a last ditch attempt to present that some aspect of what I was saying was not complete bullshit :)
15:06justin_smithnot that I got out of it with any dignity intact
15:06xuser;)
15:08justin_smithmtp: "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:08mtpjustin_smith‘ do you agree or disagree?
15:09justin_smithmtp: I have, for a personal project, taken Doubles, put them in a byte array, then extracted longs out of the byte array
15:09justin_smithI wonder if that is a counter example to what he is saying? clearly I was type punning, if in a roundabout way
15:09mtpthat is an explicit operation that converts your doubles into C's native memory model, and then out again
15:10mtpso you were type-standup-comedying. :)
15:10justin_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:13justin_smiththat it is not possible in most languages? I would say rather that it is not easy in most languages.
15:14mtpit requires two conversions
15:14mtpinstead of directly accessing the representation
15:14justin_smithhah
15:14mtpthis is the point of the article!
15:14justin_smithyeah, there are intermediate steps in my clojure version that don't need to happen in c
15:14mtpto get this behavior out of most languages, you have to do work to get things into a bytearray
15:15mtpc's memory model IS a byte array
15:16justin_smithmtp: it is really really weird to see Derrida referenced in a paper about C
15:19justin_smithOK
15:19mtp:)
15:21cYmenjustin_smith: How do I redefine them all by re-reading the source after I have made changes?
15:29justin_smithcYmen: (require 'your.ns :reload)
15:30justin_smithfrom the repl
15:30cYmenI 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:31cYmenjustin_smith: Thanks, that is super useful.
15:35justin_smithcYmen: because ring is likely not looking up the value of handler on each request
15:35justin_smiththere is a trick using #'handler which forces it to resolve it on each new request
15:36cYmenhm
15:36justin_smithalso there is middleware like wrap-reload that automatically reloads any changed files on each request
15:36justin_smithcYmen: 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:37justin_smithI only know the details because I helped write the framework
15:37cYmenI think I just want to reload the entire file every time.
15:38cYmenCoding in the repl isn't pleasant anyway.
15:50justin_smithcYmen: it can be with proper editor integration
15:50justin_smithbut yeah, there is the wrap-reload handler if you want that
15:51cYmenI'm actually fine with manually reloading if it is just two keypresses in the repl.
15:51justin_smithhttp://clojuredocs.org/ring/ring.middleware.reload/wrap-reload
15:56justin_smithuparrow / return
16:10TheLastProjectHey 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:10TheLastProject"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:16justin_smithTheLastProject: why does add-clothing execute y as a function?
16:18TheLastProjectjustin_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:19justin_smithalso, 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:19TheLastProjectjustin_smith: Ah, that is what the whole "unmutable" deal was about. Okay, good to know that needs fixing too
16:23justin_smithTheLastProject: vectors are not mutible
16:23justin_smithyou can return a modified version, and replace a binding with that value
16:23justin_smithbut the vector itself never changes
16:24TheLastProjectjustin_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:24justin_smithI am working on a slightly easier to debug version of the last part of the code
16:24TheLastProjectAh, okay
16:24justin_smithexample:
16:25TheLastProjectI've tried literally everything there, heh...
16:25justin_smith,(let [x [1 2 3] y (into x [2])] [x y])
16:26clojurebot[[1 2 3] [1 2 3 2]]
16:26justin_smithnotice that x is unchanged, but the new binding, y, shows the result (once clojurebot responds)
16:26TheLastProjectSo for add-clothing I need to overwrite the old x with the new y
16:30TheLastProjectjustin_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:30TheLastProjectAt very least, thank you a lot for the heads-up on the add-clothing part, that would've really caused a headache otherwise
16:30coventryTheLastProject: 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:30TheLastProjectWait, no
16:31justin_smithTheLastProject: are you running the code from the same namespace where it is defined? if not the resolve will fail
16:31TheLastProjectThe resolve didn't work >.<
16:31TheLastProjectjustin_smith: I have no clue how namespaces work, honestly
16:31justin_smithtook him long enough!
16:32TheLastProjectcoventry: Atom. Need to read up on that. Thanks
16:32OtherRavenprobably should read up on namespaces too :p
16:33TheLastProjectOtherRaven: 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:33coventryTheLastProject: 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:33TheLastProjectWell, okay, it are STILL my Python days
16:33justin_smithnot with the new y
16:33justin_smithyou need to put the version with y added where it was
16:33justin_smithatoms are a standard way to do that
16:34OtherRavenTheLastProject: do you know how java classes work? 'cause namespaces compile down to java classes
16:34justin_smith(swap! x conj y)
16:34amalloyOtherRaven: no they don't
16:34justin_smith,(let [x (atom [0 1 2] y 3)] (swap! x conj y) @x)
16:34clojurebot#<CompilerException java.lang.RuntimeException: Unable to resolve symbol: y in this context, compiling:(NO_SOURCE_PATH:0:0)>
16:34OtherRavenamalloy: really? I thought they did
16:34TheLastProjectcoventry: "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:35justin_smithsome day clojurebot will show us that this actually updates x
16:35TheLastProjectWould I make a map called "dispatch" which would have things like ":add add-clothing"?
16:36coventryTheLastProject: I.e., (def commands {"add" add-clothing "count" count-clothing}) ((commands cmd) args)
16:36TheLastProjectcoventry: Ah! Of course1
16:36justin_smithcoventry: yeah I just showed him how to use an atom
16:37justin_smithor tried to
16:37justin_smithevery top level binding (created by def or defn etc.) is in a namespace
16:38coventryjustin_smith: Yeah I think between us we've covered the two biggest issues.
16:38justin_smithif 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:39justin_smithyeah, coventry's idea is good
16:39amalloyOtherRaven: 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:40justin_smith(get {"list" list-clothing "count" count-clothing} (first input-words))
16:40justin_smithwhere input-words is a local binding for splitting the input
16:41justin_smithwow clojurebot is slogging
16:41addisadenyou need to write like this
16:41addisaden,(+ 1 2)
16:41clojurebot3
16:41addisaden;)
16:41justin_smithTheLastProject: I just showed you how it is done in the whole (get {...}...) thing above
16:42addisaden,(get {"list" list-clothing "count" count-clothing} (first input-words))
16:42clojurebot#<CompilerException java.lang.RuntimeException: Unable to resolve symbol: list-clothing in this context, compiling:(NO_SOURCE_PATH:0:0)>
16:42OtherRavenamalloy: Fair enough. I knew there was a strong correlation between the two concepts, anyway. XD
16:42TheLastProjectjustin_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:45justin_smith,(future "time-travel high five")
16:45clojurebot#<SecurityException java.lang.SecurityException: no threads please>
16:47justin_smith,@(future "dereferenced time-travel high five")
16:47clojurebot#<SecurityException java.lang.SecurityException: no threads please>
16:47justin_smithaddisaden: I did
16:48justin_smithaddisaden: that was not an attempt to use clojurebot, it had symbols that were never defined for him
16:49justin_smithmy other attempts to use clojurebot lag a lot, for some reason
16:49justin_smithmaybe he thinks I am overusing his services
16:50justin_smithTheLastProject: sure - remember you can build up definitions in the let block :)
16:50justin_smith,"Eventually"
16:50clojurebot"Eventually"
16:52justin_smithsee! that was my ironic call to future above
16:53TheLastProjecthttp://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:53TheLastProject(rest...)) returns every letter of upper on its own line in "list upper"
16:54TheLastProjectI'm probably missing something really simple which I just didn't copy over correctly from what you two said >.<
16:58amalloyTheLastProject: you want (apply (dispatch ...) (rest ...))
17:00amalloyapply takes a function and a list of arguments, and calls that function with the arguments "unwrapped"
17:01TheLastProjectamalloy: 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:02amalloymmm. then i guess i didn't read something carefully enough
17:02TheLastProjectI 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:03TheLastProjectHeh, 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:04OtherRavenTheLastProject: that's what insomnia is for
17:04TheLastProjectOtherRaven: I don't know if I can afford that, though. I'm already terribly tired every day
17:04justin_smithamalloy: does he? list-clothing expects a list as input
17:06TheLastProjectjustin_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:06justin_smiththough this will not work for add-clothing
17:06justin_smithbut add-clothing is still off, because upper is not an atom
17:07coventryI wish things like read-line played nicer with nrepl.el.
17:07justin_smith(assuming upper is where you wanted to store the accumulated added clothing)
17:07TheLastProjectjustin_smith: "Because upper is not an atom" Hmm, I really need to figure out what I'm messing up now
17:07TheLastProjectYeah, I do
17:07TheLastProjectI think I misunderstood you before
17:07OtherRavenTheLastProject: 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:07coventryTheLastProject: http://clojure.org/atoms
17:08justin_smithamalloy: yes, and list-clothing takes one arg, a list
17:08justin_smithand is being called with one arg, a list
17:08TheLastProjectOtherRaven: Too late to fix that, heh...
17:08justin_smithso why use apply?
17:08OtherRavenTheLastProject: Tehehe
17:09justin_smithTheLastProject: are you passing in "upper" and expecting that to resolve to the var upper?
17:09justin_smithTheLastProject: that isn't quite how clojure works, I would suggest another lookup, the same way you look up the function
17:10justin_smithif you type upper into read-line, yes, it will be a string
17:10TheLastProjectjustin_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:11justin_smithTheLastProject: the early mistakes are the hardest, if you stick with it it all makes sense
17:11justin_smithclojure has different rules than most languages, but its rules are more self-consistent
17:11justin_smithand it has fewer of them
17:12OtherRavenTheLastProject: One thing that's good to do before sleeping is reading programming manuals. *hint hint*
17:12bitemyapp`cbp: looks good to me, thanks!
17:13justin_smithTheLastProject: 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:13justin_smithif they were implicitly interpreted you would get very weird bugs
17:13justin_smithsorry, "upper"
17:13justin_smithso 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:13TheLastProjectjustin_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:14TheLastProjectAh, I got it working
17:14TheLastProjectI'm sure there must be a better way, but this works for the time being
17:14justin_smithtry this: (def collections {"upper" [...]}) then you can do a lookup on the other arg the same way you would with the first
17:15justin_smithcoventry: yeah, I had to modify his code to play with it at all
17:15TheLastProjecthttp://pastebin.com/6vGq7tpM
17:15TheLastProjectGuess this is what it is for now
17:15justin_smithTheLastProject: an atom is used to store a value you want to update safely
17:15justin_smithTheLastProject: it seemed like upper was something you wanted to be able to update
17:16TheLastProjectjustin_smith: Yeah, I read the atom page, but my mind is unable to parse it at the moment
17:16justin_smithin this case, amalloy was right, you do want to use apply, so that x and y can be separate args in update-clothing
17:17TheLastProjectAh...
17:17justin_smithand 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:17TheLastProjectI'm probably just too tired to figure out the right way to do this
17:18coventryTheLastProject: 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:18justin_smith(btw "amalloy was right" is a truism)
17:18justin_smithsure, learning takes a while
17:18OtherRavenyes, programming manuals are good
17:19justin_smithand no need to learn everything in one day, of course
17:19TheLastProjectI always try to avoid those manuals because I find them boring, heh...
17:20OtherRavenReally? I find them entertaining.
17:21OtherRavenAnd if there's any language that's fun to read about, it's clojure.
17:22justin_smithoh, you want the DWIM instruction
17:22justin_smithI think that's a perl thing :)
17:22amalloyjustin_smith: an emacs command, too: comment-dwim
17:22justin_smithnice
17:22bitemyappTheLastProject: Clojure Programming is a pretty good book and will make certain you don't make unnecessary mistakes.
17:23TheLastProjectDWIN?
17:23amalloyaw, no such thing as unnecessary mistakes. every mistake you make while learning is something you needed to learn not to do
17:23amalloyTheLastProject: do what i mean
17:23TheLastProjectHeh...
17:24bitemyappamalloy: I suppose, but I see a lot of people "grind" in here.
17:24justin_smithTheLastProject: it is a decent start
17:25justin_smithmaking 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:27justin_smithand check out "clojure programming" from oreilly and "the joy of clojure"
17:27TheLastProjectOkay, I get it, I'm not getting out of reading :P
17:28justin_smiththose books are not manuals
17:28justin_smiththe manual is right in the repl
17:28OtherRaventhe joy of clojure is nice, but too hard for a beginner
17:28justin_smith(doc fn)
17:28clojurebot"([& sigs]); params => positional-params* , or positional-params* & next-param positional-param => binding-form next-param => binding-form name => symbol Defines a function"
17:28OtherRavenor at least it was too hard for me when I tried to read it XD
17:28justin_smith(source fn)
17:28justin_smithyou can learn a lot just by calling source on various things
17:29TheLastProjectThat's an actual clojure command?
17:29justin_smith,(doc doc)
17:29clojurebot"([name]); Prints documentation for a var or special form given its name"
17:29bitemyapp,(doc source)
17:29clojurebot"([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:29OtherRavenfunction, yeah
17:29bitemyapp,(source doc)
17:29clojurebotSource not found\n
17:29bitemyappalright fine.
17:29TheLastProjectHeh, \n
17:29amalloy~def doc
17:29amalloythat's clojurebot-specific, though
17:30justin_smithin about fifteen minutes clojurebot will respond with "who's there?"
17:31justin_smithamalloy: heh
17:32justin_smithTheLastProject: DWIM - do what I mean
17:33justin_smithgrind?
17:37justin_smithdid I say 15 minutes? guess it is more like 7
17:38justin_smithTheLastProject: yeah, doc
17:38justin_smithin your own repl, doc and source will work
17:39justin_smiththey are often as much manual as you need
17:39justin_smithamalloy: doc and source are standard
17:39justin_smith?
17:39justin_smithbut ~def, yeah
17:51justin_smithan eloquent insight on learning to program: http://i.imgur.com/cBComRu.png
18:04bitemyappjustin_smith: perfect.
18:32dotemacsHi, 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:33dotemacsthe problem i'm seeing is that query-string within the request has the 'word=tiger' yet, the params within the request is blank
18:34dotemacswhat am I doing wrong here?
18:35danielszmulewiczMe says you need the wrap-params middleware
18:35danielszmulewicz:-)
18:37danielszmulewiczdotemacs: http://www.refheap.com/20698
18:43danielszmulewiczchange defroutes app to defroutes routes
18:44danielszmulewiczthen define app like in the refheap
19:12bitemyapparrdem: where did the pipelining stuff come from?
19:21Morgawrtrivial 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:22Morgawrso I can always be sure that sending stuff to that agent will never leave it in FAILED mode
19:23bbloomMorgawr: should be trivial to test that
19:25Morgawrbbloom: you're right, I was just wondering about irky situations like IO operations
19:25Morgawrbut I can test it
19:26Morgawrmmm, IO is still executed but when the exception is encountered it's just ignored and the entity's state it kept intact
19:26Morgawrokay, that's a good explanation for me :P
19:27bitemyappMorgawr: I just got done figuring a way to handle agent error modes and finalization
19:28bitemyappMorgawr: in Revise, specifically.
19:28Morgawrbitemyapp: do tell :)
19:30bitemyappMorgawr: well in this case I made a somewhat unusual connection management strategy for a database client library.
19:30bitemyappMorgawr: agents were serializing access to the IO resource, in this case a socket and its associate in and output streams.
19:31bitemyappMorgawr: sockets can die, lose connection, yadda yadda.
19:31bitemyappin 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:31Morgawrinteresting, yeah
19:31bitemyappthere's no point mutating the agent with a new socket in an attempt to recover, callers need to decide what's appropriate.
19:32Morgawrhow do you notify the caller of the agent?
19:32bitemyappin 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:32bitemyappMorgawr: promises.
19:32bitemyappall query results happen through promises. There's an independent (as in, doesn't happen in the agent) reader thread for the socket too.
19:33Morgawrah yeah
19:33Morgawrmakes sense
19:33bitemyappMorgawr: for more, see: https://github.com/bitemyapp/revise/
19:34Morgawrbitemyapp: I'll give it a look, thanks
19:48bitemyappddellacosta: welcome.
19:49ddellacostamorning
19:52bitemyappddellacosta: dunno if you saw, but I ended up coming up with a satisfactory solution to the error handling thing last night.
19:54Morgawrmm... I'm trying to use a "PrintWriter" from java (java.io.PrintWriter) but I can't quite get it to work with clojure
19:54MorgawrI try to do java.io/PrintWriter
19:54Morgawrbut it says it can't find the class
19:54Morgawrisn't that how it should work? or do I have to import it too?
19:54arrdembitemyapp: in what sense?
19:55arrdembitemyapp: as in where did I learn that stuff, or as in what's the reason for it?
19:55arrdembitemyapp: or just why am I writing about it?
19:56amalloyMorgawr: the / is for separating classname from method
19:56amalloythe classname is just java.io.PrintWriter
19:56Morgawramalloy: ah.. I see
19:56Morgawrthanks
19:58ddellacostabitemyapp: sorry, I did see it but got busy at the very end of things and didn't respond. :-(
19:59ddellacostabitemyapp: 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:06bitemyapparrdem: I'm just curious about it in general.
20:06bitemyapparrdem: also, up for a game?
20:07muhoomonads can be demanding. climbing up on your bed and pawingnat you because they want to be fed
20:07arrdembitemyapp: I think I can get a box... lemme check
20:07brehautmuhoo: lolwat!
20:08muhoobrehaut: ddellacosta wanted to know why something demanded a monad
20:08muhoodamn monads, always making demands
20:09ddellacostadamn you, monads
20:09ddellacostathere ya go
20:09arrdemI shoulda used monads in that pipeline post...
20:09arrdemthe vm state is really a monadic value...
20:09muhoomonads always sounds to me like a body part you wouldn't want injured
20:10brehautif injured badly enough, you may be left with only a monoid
20:10bitemyapparrdem: it is indeed.
20:13bitemyappddellacosta: I've acquired some materials on category theory, trying to form a more thorough understanding of how it all fits together.
20:14brehauti 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:14brehautwelcome to page 4
20:15bitemyappsaw an ordering (order by this, then that, then this) abstraction defined in terms of a Monoid. really tickled me.
20:15brehauthah yeah
20:15bitemyappbrehaut: what book was this? the one I'm reading isn't bad so far.
20:16brehautBasic Category Theory for Computer Scientists
20:16bitemyappI'm reading "A Gentle Introduction to Category Theory - The Calculational Approach"
20:16bitemyappthey're a bit vague about morphisms (I'm on page 8), but things are going okay so far.
20:16brehauti think morphisms are just anything that changes a category structures shape
20:17brehauteach morphism is quite distinct
20:17brehauti found homomorphisms (on monoids anyway) to be relatively grokable and interesting
20:18brehautbasically (mappend (H a) (H b)) === (H (mappend a b)) (where H is a homomorphism)
20:18brehauts/H is a/H is any/
20:18mtpthat smells like the distributive law
20:19brehautmtp it might be, but its specifically defined in the context of categories
20:19arrdembitemyapp: the gaming box is currently being used for stupid shit. I'll let you know if I manage to get an exclusive lock.
20:20brehautor monoids at least
20:20bitemyapparrdem: mutex lol
20:20brehautso there are many Monoids around numbers, and around strings or lists
20:20bitemyappanything concatenative or progressively accumulated, yeah.
20:20sritchiebrehaut: fun time to come in :)
20:20brehautyeah
20:20brehautsritchie: haha :) im reaching the end of what ive learnt
20:21bitemyappinteresting from a distributed systems perspective is to what degree you can formalize "independence" between operations performed.
20:21bitemyapponce 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:21sritchieI was playing around with simple-check and the monoid laws the other day -
20:21sritchiehttps://gist.github.com/sritchie/7406280
20:21brehautyes exactly. and you can trivially rewrite any expression between monoidal structures via homomorphism to order as your resources allow
20:21bitemyappimagine if every query was a black-box program that mutates the state of the database?
20:22bitemyappinstead of something declarative.
20:22bitemyappbrehaut: one moment of minor confusion I've had is that this book defined fold as being commutative.
20:22bitemyappthat was a bit ?_?
20:23bitemyappI guess that's true in some cases (reduce +), but it still threw me for a loop briefly.
20:23brehautbitemyapp: i thing that if you define 'fold' as distinct from 'left fold' and 'right fold' then it may be true?
20:24brehaut(isnt that what the reducers library does?)
20:24bitemyappbrehaut: I think so, yes.
20:24amalloybrehaut: yes
20:25brehautsritchie: *brain asplode* ;)
20:25sritchieI want to encourage more monoids in Cascalog,
20:26sritchieso users can write more general aggregations, then just extend the traits to their particular datastructures
20:26brehauthuh thats interesting
20:26sritchiemaking it trivial to move on from counts, to cardinality estimations, to moving averages, etc
20:27sritchieSummingbird was all about encouraging users to use values with Monoids defined;
20:27sritchiehttps://github.com/twitter/summingbird
20:28sritchieas you were saying, we can get pretty far with systems optimizations knowing algebraic properties alone
20:28sritchieassociative - boom, we can add realtime and batch computed values together into one seamless, realtime looking value
20:28sritchiecommutative gets you more optimizations, like mapside aggregation; and so on.
20:30coventryIs there a version of clojure.walk which preserves all metadata? E.g.
20:30coventry,(require '[clojure.walk :as wk])
20:30clojurebotnil
20:30coventry,(->> [(with-meta (list 1) {:foo true})] (wk/postwalk identity) first meta)
20:30clojurebotnil
20:31brehautsritchie: thats cool stuff
20:32brehautsritchie: 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:32sritchieyeah man, some of the most fun stuff to work on has been the graph optimization layer -
20:33sritchiewriting 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:34amalloycoventry: no
20:34brehautsritchie: 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:35sritchieyeah, especially when you get into the whole "monad is a monoid in the category of endofunctors" set of one-liners
20:35coventryamalloy: Thanks. Any suggestions for a traversal framework which does? I am planning to try zippers next.
20:35brehautsritchie: that one at least was intended as a joke i believe
20:35sritchie:)
20:35brehautsritchie: but yes, it makes people blanch
20:35sritchiebut no one knows it!
20:36brehautsritchie: have you ever asked fsbot in emacs about monads and comonads?
20:36brehautin #emacs
20:36sritchiebrehaut: have you played with any of the approximate data structures that have been coming out, like hyperloglog, count min sketch, etc?
20:36sritchiehaha, no
20:36brehautits worth joining just to try it
20:36amalloynothing i know of, coventry
20:37sritchie"[Too many DB matches] Accent on helpful side of your nature. Drain the moat."
20:37sritchie:)
20:38brehauthaha
20:38bitemyappbrehaut: I just tried it, hahahahaha
20:38bitemyappsritchie: yeah brehaut is right, it's worth trying.
20:39brehautthe co- stuff in category theory made a lot more sense to me once i realised categories were all about topologies
20:39brehaut(and that it just swaps the domain and codomain for the various arrows)
20:39sritchiebrehaut: 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:40sritchiemy mind was blown - now, after the Scala years, it's all clear
20:40sritchiebut I haven't gone after comonads
20:40brehautsritchie: haha cool. i vaguely recall now too.
20:40brehautive looked at comonads from a distance
20:40sritchieI ended up writing a thrift parser using a parser monad:
20:40sritchiehttp://github.com/sritchie/thrift-clj
20:40bitemyappI 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:41sritchiebrehaut: a la your tutorial
20:41brehautoh right. cool :)
20:41amalloyi've briefly come to an understanding of comonads, a few times
20:41bitemyappamalloy: slippery.
20:41brehautsritchie: im curious how the performance is
20:42brehautand im glad to see you switched to instaparse
20:42sritchiebrehaut: I wanted to compare it to an instaparse version via mark engelberk
20:42sritchieengelberg
20:42sritchienow that I'm gone from Twitter working clj full time, it's more likely to happen
20:42justin_smithhey brehaut, you said you do web stuff with ring, right?
20:42brehautjustin_smith: yeah
20:42bitemyappI think most Clojurians, when and if they do web stuff, do it in Ring :)
20:43brehautone hopes ;)
20:43sritchieI've been playing with liberator, btw
20:43sritchieI was asking this earlier -
20:43sritchiethough I'll stop if no one here knows liberator
20:43sritchieit feels like there should be a built-in way to define base behaviors, then define resources off of merged maps of decision points
20:43sritchieeasy to write, of course
20:44brehautso far ive only looked at liberator from the outside. havent had a need to use it in anger
20:44justin_smithbrehaut: you may be interested, I made a ring middleware to store, retrieve, and replay ring requests https://github.com/noisesmith/groundhog
20:45brehauthaha i love the name
20:45bitemyappjustin_smith: that's pretty cool
20:46bitemyappnow I want to watch that movie.
20:47bitemyappjustin_smith: for the body, which might be binary, why not base64?
20:48bitemyappoh you did a byte-array anyway, n/m
20:51john2xwhat's the best way to replace all `x` values in a 2D grid with `y`?
20:52arrdemjohn2x: how's the grid represented?
20:55justin_smithheh
20:56justin_smithI left it kind of wide open in terms of storage if you want anything other than a global atom in the lib itself
20:56justin_smitheventually I may add some helper functions for db storage file storage etc.
20:56justin_smithbut it's just edn so I figure people can roll their own
20:56john2x[[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:57justin_smithbitemyapp: it is base64
20:57justin_smithwith the reverse automatically being done on the way out
20:58justin_smithyeah, I wanted to keep strict method compatibility, so any middleware would get exactly the kind of data they expect
20:58justin_smithwithin reason
20:58egosumhmmm, anyone have any tips for nicely formatting JSON? I'd like to emulate something like this: http://jsonformatter.curiousconcept.com/
20:58justin_smithdon't pass it a long arbitrary stream in the request body and expect anything sane :)
20:59john2xarrdem: [[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:59justin_smithie. if we read faster than you write, we assume you are done and we store what we have so far
21:01arrdemjohn2x: you'll need two mapv applications, identity, contains? and a map.
21:01eseharanick esehara
21:02arrdembitemyapp: not getting a box tonight
21:02justin_smithjohn2x: I bet there is a concise way to do that with for
21:02arrdemjustin_smith: not really unless you want to ... oh. yeah there probably is.
21:03john2xjustin_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:04arrdemjohn2x: no you can definitely do this using only for. for is lazy. the for application you're thinking of won't work.
21:05john2xOT: hmm is it just my client or is arrdem's mentions of me sending doubles?
21:07arrdemjohn2x: ... wat
21:07eseharahi, Clojuren. I'm developer in Japan. I wish to translate official document to Japanese. Do you have good approach about it?
21:08bitemyapparrdem: nuts.
21:08brehautjohn2x: no its just you
21:08brehautjohn2x: no its just you
21:08john2xheh. I guess it's just me. Right, I keep on forgetting that for is lazy. How would I approach this for?
21:08bitemyappjohn2x: no idea what you're talking about
21:08bitemyappjohn2x: no idea what you're talking about
21:08john2xah, brehaut's mentions are in doubles too
21:08john2xeveryone's!
21:08brehautwe are messing with you
21:09llasramPlan succeeded?
21:09john2xlol
21:10brehautapparently im too honest to gaslight
21:10egosum(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:10justin_smithjohn2x: ((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:10arrdemjohn2x: ok I lied. you need two fors.
21:10arrdemjohn2x: but it is doable with only for.
21:11justin_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:11clojurebot((nil nil nil "foo" nil) (nil nil "bar" nil nil))
21:11amalloyjustin_smith: ##(doc replace)
21:11lazybot⇒ "([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:11egosumOh my, Chesire does that… https://github.com/dakrone/cheshire
21:11arrdemjohn2x: https://www.refheap.com/20707
21:12arrdemamalloy: damnit
21:12justin_smithjohn2x: 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:12justin_smithalso ignores input not in the mapping
21:12justin_smith(passes identity)
21:13justin_smitharrdem: ?
21:13amalloyweird that replace is special-cased for vectors to return another vector
21:13amalloyrather than just returning a seq in all cases
21:13justin_smithjohn2x: yeah, the version I posted aboce is pure functional
21:13justin_smithfor is not imperative in clojure
21:14justin_smiththe coincidence that it has the same name as the default imperative mutate a collection construction is too bad
21:15amalloyjustin_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:15amalloyclojurebot's slow responses to you support the first theory
21:15justin_smitharrdem: 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:16justin_smithjust you
21:17amalloyyeah, definitely bad internet connection. clojurebot responded to you in one second
21:18arrdemjustin_smith: oh. right. I saw that you posted a solution but I didn't really read it.
21:18arrdemjustin_smith: you have bested me, brb falling on my perentheses
21:19justin_smithjohn2x: you would approach it in exactly the way I pasted above, it is working code that produces the output you asked for
21:21amalloy&(map (partial replace ["foo" "bar"]) [[nil nil nil 0 nil][nil nil 1 nil nil]])
21:21lazybot⇒ ([nil nil nil "foo" nil] [nil nil "bar" nil nil])
21:21amalloyis rather nicer imo
21:23justin_smithamalloy: good point
21:24justin_smithhandy
21:26justin_smithamalloy: ok, something is messed up with my internet clearly
21:26justin_smiththat would explain the lag of clojurebot's responses
21:26justin_smithand people acting like they haven't heard me
21:28justin_smiththanks for helping me figure that out, it was kind of gaslighting me :)
21:29justin_smitharrdem: lol
21:30john2xawesome. thanks guys. I'm really finding it hard to think differently about `for`.
21:31arrdemjohn2x: 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:34john2xyeah, but even then, there's no way I could've thought about using map, partial, replace to achieve what I wanted.
21:34hyPiRionIt's about training
21:34amalloypractice, practice, practice
21:35amalloyarrdem: i don't think foreach is a meaningfully different name
21:36john2xyeah. 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:37arrdemamalloy: 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:41justin_smithjohn2x: I wish they had picked a different name for 'for'; like maybe 'cartesian'
21:43justin_smith,(for [x (range 4) y (range 4)] [x y]) ; cartesian
21:43clojurebot([0 0] [0 1] [0 2] [0 3] [1 0] ...)
21:44hyPiRion'for' is confusing because it's used differently in imperative languages
21:46hyPiRionThere is one function though... `contains?` anyone?
21:46seangrovecontains?
21:46clojurebotcontains? 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:47seangroveLove me some `contains?`, find myself reaching for it much sooner these days
21:48hyPiRionoh, humm...
21:49hyPiRion,(let [a (doto (java.util.ArrayList.) (.add 10)) b (java.util.ArrayList.) coll #{a b}] (.add b 10) coll)
21:49clojurebot#{[10] [10]}
21:50hyPiRionobviously, don't do that.
21:54bitemyappI use contains? whenever the query I am performing makes sense in an associative context
21:54bitemyapparrdem: wait, so can you play LoL without borrowing a box, or was that for general purposes?
21:55hyPiRionyeah, I only use it on maps and sets
21:56bitemyapphyPiRion: 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:58arrdembitemyapp: that was for general purposes. I haven't tried running sc2 or lol under Arch but it's worth a shot.
22:02justin_smithhyPiRion: woah, that is, like, spooky
22:03bitemyapphyPiRion: why does the ArrayList look like a vector?
22:03bitemyapphrm, so you can mutate references inside of immutable collections. Yeah, that's not a fantastic idea.
22:04hyPiRionjustin_smith: not really. It's just that maps cannot update themselves (and shouldn't) when "values" update
22:05hyPiRionbitemyapp: not sensible for mutable collections either!
22:42dobry-denSeesaw is amazing
22:46bitemyappucb: http://www.youtube.com/watch?v=9vL9zCFpv-0
22:46arrdembitemyapp: sc2 under Arch was the failz
22:46bitemyappucb: sorry if I've already sent it to you, I thought of it and figured it was good for you.
22:46bitemyapparrdem: attempted Wine I take it?
22:46arrdembitemyapp: with unfortunate results.. part of ~/ got rm'd.
22:47bitemyapp :|
22:47bitemyappdafuq kinda amateur hour...
22:48coventryHow does that happen?
22:50arrdemnothing lost tho... ~/config bit it, but that's under version control. restoring now... everything else that got hit is symlinks so no problem.
22:53coventryOh, good. It'd really suck if an application misbehaved that way.
22:55TimMcarrdem: `rm -r ~/ .wine`?
22:55arrdemTimMc: something like that, yeah.
22:55TimMcOuch.
22:56arrdemTimMc: C-c saved the day, and git more crontab'd backups are making everything right.
22:56arrdemI'm just suffering from amature hour as bitemyapp was kind enough to remind me.
22:56hyPiRionI'll give you guys a great trick I've learnt
22:56coventryI've made the same mistake, although a long time ago. I generally do an ls first, then back in the rm -rf.
22:56TimMcMay I recommend installing trash-put and aliasing rm to it?
22:57bitemyapparrdem: amateur*
22:57arrdembitemyapp: go away
22:57TimMcIt was a dick thing to say.
22:57bitemyappTimMc: I was actually assigning the blame to Wine.
22:57hyPiRionI 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:57bitemyappTimMc: it was his confession that made me aware the rm'ing of his ~ was his fault.
22:58coventry"rm bitemyapp" "rm: cannot remove `bitemyapp': Permission denied"
22:58TimMcbitemyapp: Ah, that's an important distinction.
22:58TimMcYou're thinking of e.g. the bumblebee incident?
22:59TimMchyPiRion: Nice.
23:00technomancyjustin_smith: looks like you forgot to license groundhog?
23:00technomancyalso :source-paths in project.clj is redundant
23:00TimMcls: unrecognized option '--false-flag'
23:01TimMcbitemyapp: https://github.com/MrMEEE/bumblebee-Old-and-abbandoned/issues/123
23:03TimMcOr rather https://github.com/MrMEEE/bumblebee-Old-and-abbandoned/commit/a047be85247755cdbe0acce6#diff-1
23:05bitemyappTimMc: sigh, troll threads on github.
23:05justin_smithwasn't it npm that started removing people's directories for one of its updates?
23:05bitemyappjustin_smith: to be fair, disabling the computers of node.js users is a service to mankind.
23:06bitemyapp<SHOTS_FIRED.JPG>
23:06justin_smithhttps://github.com/isaacs/npm/issues/2293 maybe is what I was thinking of?
23:07bitemyapp"and I enjoy reinstalling things :)" <--- stockholm syndrome
23:08justin_smith,'HELLO
23:08clojurebotHELLO
23:08justin_smithok, it was emacs' fault
23:37muhoosure, blame emacs
23:37justin_smithwell, the restart fixed the issue, so..
23:42muhooTimMc: holy shit. github->4chan