2013-11-10
| 00:00 | bitemyapp | algal: don't. |
| 00:00 | bitemyapp | algal: either your SQL database is your point of truth or it isn't, don't try to be clever. |
| 00:00 | algal | bitemyapp: okay. what should I do instead? (honest question.) |
| 00:00 | bbloom | algal: what database are you using? |
| 00:00 | algal | postgresql |
| 00:00 | bitemyapp | algal: what sort of invariants are you talking about? |
| 00:00 | bitemyapp | algal: just write simple code. |
| 00:01 | bbloom | ok, so an actual database :-) |
| 00:01 | algal | I've written simple code, but I'm not experienced with multithreaded programming at all so I am not confident it is correct. |
| 00:01 | bbloom | algal: here's a good place to start with databases: http://www.postgresql.org/docs/9.1/static/transaction-iso.html |
| 00:01 | bitemyapp | algal: don't try to move consistency outside of the database. |
| 00:02 | bitemyapp | algal: your database should be resolving all conflicts and handling all transactions. |
| 00:02 | bitemyapp | algal: in brief, don't try to be clever. it'll bite you no matter what language you use. |
| 00:02 | algal | Here's an invariant: I have a database table that has columns USERNAME and SESSIONID, where SESSIONID is a UUID. |
| 00:03 | algal | I want to ensure that every username has 3 or less sessionIDs. |
| 00:03 | bbloom | algal: wrap your writes in transactions and be aware that multiple independent reads are not necessarily consistent with each other. if you need consistent reads with complex multi-query processes, you need to either write stored procedures, or treat your database as append-only and use good IDs and timestamps and whatnot |
| 00:04 | bitemyapp | algal: that has to happen in the database. |
| 00:04 | bitemyapp | algal: what bbloom said precisely. |
| 00:04 | bbloom | algal: alternatively, you can do something simpler: allow them to go over 3 sessions, but afterwards, kill the oldest N-3 sessions |
| 00:05 | algal | bbloom: Thanks, this is very helpful. When you say "wrap your writes in transactions", you mean all within postgresql right? |
| 00:05 | algal | bbloom: (not STM transactions) |
| 00:05 | bbloom | algal: right. if you issue multiple mutations from your client, you want to wrap them in a database transaction |
| 00:06 | bbloom | that's not the default in rails, for example, and people get all sorts of stupid bugs b/c of it |
| 00:06 | bitemyapp | bbloom: gotta win those benchmarks yo. |
| 00:06 | bbloom | things like `validate_uniqueness_if :username` simply *DO NOT WORK* if they are based on a read and then a write with a network round trip in the middle |
| 00:06 | algal | bbloom: Okay. Thanks. This is quite helpful. So why is it a simpler alternative to kill the oldest 3 sessions? Doesn't that complicate things by requiring me to timestamp sessions and introduce a new process that kills old sessions? |
| 00:07 | bbloom | algal: you fetch somebody's session on every request right? |
| 00:07 | bbloom | when you say SELECT * FROM sessions WHERE id = ?; |
| 00:07 | bbloom | then you only get one |
| 00:07 | bbloom | but you can do: |
| 00:08 | bbloom | INNER JOIN sessions AS self ON sessions.user_id = self.user_id |
| 00:08 | bbloom | that will give you all of the sessions for each user. if it's greater than 3, you can issue a query to clear it |
| 00:08 | bbloom | then there is no cron process |
| 00:08 | bbloom | if multiple threads all try to drop all older than 3, then the behavior is *idempotent* |
| 00:09 | bbloom | there is no need for a coordinated read/write pair to prevent going over 3 |
| 00:09 | bbloom | if you want to reeeaaally prevent going over 3, you need a stored procedure |
| 00:09 | bbloom | your very first stored procedure has a very high complexity cost, b/c now you are versioning your code INSIDE your database |
| 00:09 | bbloom | if you can get away with no stored procedures, that's great, but realistically you'll need some in a large app |
| 00:10 | algal | bbloom: Wow, this is turning out to be just as complicated as I feared. I though this was an amazingly simple requirement.. |
| 00:10 | algal | But I am quite eager to understand this fully and rigorously. |
| 00:10 | algal | Would you say the postgresql page is a good place to start? |
| 00:10 | bbloom | postgres' manuals are excellent |
| 00:11 | bbloom | another approach is to implement a trigger |
| 00:11 | bbloom | the problem is that you don't want "fully serialized" transaction isolation |
| 00:12 | bbloom | it will kill your performance |
| 00:12 | bbloom | so it's always best to come up with designs that work if they fail in the middle |
| 00:12 | algal | bbloom: So would it be correct to say here, that if I'm using postgresql, then all of Clojure's vaunted concurrency stuff is pretty much irrelevant because I'm effectively pushing all the consistency requirements into the db? |
| 00:13 | bbloom | algal: clojure's concurency primitives are for *threads*, but you're going to have *clients* |
| 00:13 | bbloom | you need consistency between machines |
| 00:13 | bbloom | your database can provide that |
| 00:14 | bbloom | that's not to say they aren't useful for other things |
| 00:14 | algal | But in a servlet (this is a ring app), isn't the issue that separate clients could be represented by separate threads? |
| 00:14 | bbloom | right, but you can treat separate threads the same was as you'd treat separate boxes |
| 00:15 | bbloom | you can think of it ass an optimization to start sharing memory & communicating between threads |
| 00:15 | bbloom | for example, if you cached some stuff in memory, an atom would be a great place to store that |
| 00:16 | algal | okay. |
| 00:16 | bbloom | bitemyapp: rails doesn't win any benchmarks ever. so i'll just s/rails/mongo/ |
| 00:17 | bbloom | i haven't read it myself, but i hear great things about http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601 |
| 00:18 | bitemyapp | bbloom: no, no no |
| 00:18 | bitemyapp | bbloom: don't recommend JCIP |
| 00:18 | algal | bbloom: Yes, I've been meaning to read that one. |
| 00:18 | bbloom | ok, nevermind then |
| 00:18 | bitemyapp | no |
| 00:18 | bbloom | lol |
| 00:18 | bitemyapp | don't. |
| 00:18 | bitemyapp | algal: don't. |
| 00:18 | bitemyapp | algal: save yourself. |
| 00:18 | algal | bitemyapp: why not?? |
| 00:18 | lazybot | algal: Uh, no. Why would you even ask? |
| 00:18 | xuser | isn't his underlying http server (jetty or http-kit) making his ring app concurrent already? without doing any manual concurrency in the clojure code |
| 00:18 | bbloom | my understanding was that it's a good intro to threading in general |
| 00:18 | bbloom | xuser: yes |
| 00:18 | bbloom | algal: basically, for the typical app, all you need to do: |
| 00:19 | bitemyapp | algal: it's a terrible, terrible book. |
| 00:19 | bitemyapp | I'm *still* angry I bought it. |
| 00:19 | bbloom | algal: 1) be *aware* that multiple reads involve multiple network round trips, and other stuff might happen in between reads |
| 00:20 | algal | bbloom: Yes, this awareness is exactly what's got me puzzled about how to do everything correctly. |
| 00:20 | bbloom | algal: 2) when doing multiple writes, subsequent writes may fail, and so you need a transaction to make them operate as a unit |
| 00:20 | bbloom | algal: when in doubt, re-read http://www.postgresql.org/docs/9.1/static/transaction-iso.html a few times |
| 00:20 | bbloom | algal: "Read committed" is the default and almost 100% definitely what you want unless you really know better |
| 00:21 | bbloom | algal: see http://www.postgresql.org/docs/9.1/static/server-programming.html for doing stuff "over there" on the server, if you need to for better consistency |
| 00:22 | bbloom | algal: and watch Rich Hickey's "are we there yet" video for an explaination of perception :-) |
| 00:22 | bbloom | that's about it |
| 00:22 | bbloom | if you do that, your app will be 100X better than every rails app ever |
| 00:22 | bbloom | you've made an excellent choice to use an actual database |
| 00:22 | bbloom | don't use an ORM |
| 00:23 | algal | bbloom: Does Korma count as on ORM? I'm using clojure.java.jdcb 0.2.x, but mostly because I wanted to understand what was happening. |
| 00:23 | algal | But I was wondering if Korma would be a better way to handle it. |
| 00:23 | xuser | algal: korma is just a dsl I think, sugar |
| 00:23 | bitemyapp | Korma ain't an ORM. It's a DSL for SQL. |
| 00:23 | algal | ok. |
| 00:24 | bbloom | ehh... there are some ORM-ish things in there |
| 00:24 | bbloom | but it's certainly no active record |
| 00:24 | xuser | algal: and it probably uses clojure.java.jdbc under the hood |
| 00:24 | algal | You said I don't want "'fully serialized' transaction isolation". Does that just mean "fully locking the database for use by only one client during the scope of my operation"? I wonder if this might be okay for me. I have very low performance requirements. I am mainly worried about correctness. |
| 00:25 | bbloom | algal: well then, it's time for me to link to my favorite blog post of mine: http://www.brandonbloom.name/blog/2013/06/26/slurp-and-spit/ |
| 00:26 | algal | thanks, will have a look right now. |
| 00:27 | swarthy | bbloom: reading this, love the title on your site |
| 00:27 | bbloom | swarthy: thanks! |
| 00:28 | bitemyapp | bbloom: yeah I like that too. |
| 00:29 | bbloom | I was shouting that at my computer one day in college working on a project & my roommate told me it would be a good name for a book, if I ever wrote one day |
| 00:31 | algal | bbloom: Anything you'd recommend besides postgreSQL docs and Goetz's book? I'm late to the part on this stuff but I'd like to understand it properly and finally, rather than swim around vaguely for years. |
| 00:32 | bbloom | algal: so i had the (un)lucky experience of maintaining an *ancient* win31 app w/ a crazy custom database at some point early in my career, so i picked up a lot of database knowledge that way... but i'd guess that if there is something worth reading about this, it's on rich's bookshelf... |
| 00:32 | clojurebot | c'est bon! |
| 00:33 | bbloom | http://www.amazon.com/Clojure-Bookshelf/lm/R3LG3ZBZS4GCTH |
| 00:33 | algal | good thought |
| 00:34 | bbloom | if i ever move someplace w/ a few extra hundred square feet, i may buy some of those books :-P |
| 00:34 | algal | a hundred spare months is the real bottleneck, I reckon. |
| 00:35 | bbloom | who said anything about reading them? |
| 00:35 | algal | :) |
| 00:37 | swarthy | bbloom: oh wow this is cool, going through your old posts I found the 'Read the Source' post. I remember reading this when it came out! |
| 00:38 | bbloom | swarthy: nice! i'm a nameless celeb! |
| 00:38 | bbloom | i'm pretty sure atwood owes me royalties, but i'll let it slide :-P |
| 00:38 | swarthy | ahah |
| 00:44 | bbloom | algal: so is your app a candidate for spit & slurp? :_) |
| 00:44 | swarthy | bbloom: sorry to pry but I'm reading through a bunch of your old posts and liking them. I notice you had a startup, and LinkedIn says consulting? Using clojure for those? |
| 00:45 | bbloom | swarthy: sadly not much |
| 00:46 | algal | oh, it would be based on data volume and data complexity. But I need to use postgresql for non-functional reasons (a client just likes it). |
| 00:46 | algal | Also, I'm deploying on EC2 and I have the impression that EC2 instance's filesystems are not to be relied on for durability. |
| 00:46 | swarthy | cool. Had you already built it with other stuff? |
| 00:46 | bbloom | well, at least your client has good taste :-P |
| 00:46 | algal | bbloom: :) |
| 00:47 | muhoo | didn't chris write something called simpledb that is basucally spit/slurp an atom as edn? |
| 00:47 | bbloom | somebody did |
| 00:48 | algal | SimpleDB is the name of one of Amazon's nosql services. or is this a different one? |
| 00:48 | bbloom | different |
| 00:48 | bbloom | i think it was alandipert |
| 00:48 | allenj12 | hey if i have a list of items i want to remove from another list, how would i do that? |
| 00:49 | bbloom | https://github.com/alandipert/enduro |
| 00:49 | bbloom | allenj12: how large are your lists? |
| 00:49 | justin_smith | allenj12: (remove (set unwanted) all) |
| 00:49 | bbloom | yeah, what justin_smith said, for most use cases |
| 00:49 | justin_smith | (that is if you want to remove all instances of specific values) |
| 00:49 | bbloom | unless nil or false are valid values in your lists |
| 00:49 | lpvb | technomancy: can I start lein up and then issue commands so I don't have to wait for the jvm to start up? |
| 00:49 | algal | bbloom: Oh hello, this endure choppy can use postgresql as a backing store.... |
| 00:50 | allenj12 | ahhh kk i thought that only worked for single cases |
| 00:50 | allenj12 | ty |
| 00:50 | bbloom | algal: eh. if you're going to use postgres, use postgres |
| 00:50 | justin_smith | allenj12: a set acts as a function that returns its input if it is in the set, or nil if it is not in the set |
| 00:51 | allenj12 | ooooo kk |
| 00:51 | justin_smith | allenj12: if nil or false are valid values that you do not want to remove, you could use contains? (remove (partial contains? (set unwanted)) all) |
| 00:51 | algal | bbloom: yeah, it's probably just write text into a field.. |
| 00:53 | allenj12 | justin_smith; no the first will do but that is good to know ty very much |
| 00:54 | algal | bbloom: great post, btw.. |
| 00:54 | bbloom | algal: thanks |
| 00:55 | algal | I totally hear you w/r/t/ to rediscovering simplicity ten years later. :) |
| 00:55 | bbloom | oh yeah :-) |
| 00:55 | bbloom | every good dev has their OOP class hierarchy days, and their IT MUST SCALE TO A BAJILION days |
| 00:56 | bbloom | and a few other phases w/ perfect coding standard and whatnot |
| 00:56 | bbloom | but clojure is about getting shit done & in this room, whether or not you're using clojure or the cool-shit-de-jour, you're gonna get some fucking simplicity up in here. |
| 00:56 | algal | bbloom: Well, we grow up with what's in the air. |
| 00:57 | algal | bbloom: And late 90s was full of mis-emphasis on large scale and class tangles. |
| 00:57 | algal | anyway, that's I how remember it. :) |
| 00:57 | bbloom | algal: i recently saw some C# code i wrote while at msft. i was like "did i really document my functions like that?" |
| 00:58 | john2x | i'm trying to make a fn that gets synonyms of a words via an api, but it seems the `for` form isn't going through `words` (http://vpaste.net/AH9fD) |
| 00:58 | bitemyapp | my code 6 months ago was terrible. |
| 00:58 | bbloom | john2x: for is lazy |
| 00:58 | bbloom | john2x: you should not have side effects inside lazy operations |
| 00:59 | algal | my code 6 days ago did not think through data consistency. so we you never stop learning. maybe because I never stop forgetting. :| |
| 00:59 | bbloom | john2x: move your swap outside the for |
| 00:59 | bbloom | you can do something like: (swap! word-syms assoc (for ... )) |
| 00:59 | bbloom | or rather: |
| 00:59 | bbloom | (swap! word-syms assoc (concat (for ... ))) where your for returns [key value] pairs |
| 01:00 | bbloom | er dur, with an apply! |
| 01:00 | amalloy | bbloom: (apply assoc! ...) |
| 01:00 | amalloy | er, apply swap! |
| 01:00 | bbloom | (apply swap! word-syms assoc (concat (for ...))) |
| 01:00 | amalloy | although i don't know why you'd use apply assoc rather than into |
| 01:00 | bbloom | amalloy: lol duuuh |
| 01:00 | bbloom | (swap! word-syms into (concat (for ...))) |
| 01:01 | bbloom | or no concat |
| 01:01 | amalloy | bbloom: (= (concat x) x) |
| 01:01 | bbloom | ignore me |
| 01:01 | bbloom | i'm totally dumb right now |
| 01:01 | bbloom | sorry, i failed miserably. that means it is way past bed time |
| 01:01 | bbloom | john2x: listen to amalloy. he seems to be awake |
| 01:01 | john2x | thank you both :) |
| 01:01 | bitemyapp | ddellacosta: I figured out my problem. I forgot how POSIX works and now realize I have to just nuke all waiting promises whenever anything fails. |
| 01:01 | bitemyapp | aka, the nuclear option. |
| 01:02 | ddellacosta | bitemyapp: d'oh. |
| 01:02 | bitemyapp | This is a neutral result in my book. OTOH, the positive thing that happened today is that I started using Linux and XMonad again today for coding. What a joy. |
| 01:03 | bitemyapp | ddellacosta: basically the reason one was able to associate a particular point in time for the failure with a particular write with previous db client implementations is because of the blocking. |
| 01:03 | bitemyapp | the async nature of my implementation means "one or more" writers will get an exception. |
| 01:04 | bitemyapp | interestingly, because of the constant reader thread, a user could actually notice the agent was killed off before even attempting the write if they wanted. |
| 01:04 | bitemyapp | so in actuality, its "0 or more" writes will fail. |
| 01:04 | bitemyapp | which is interesting. |
| 01:04 | bitemyapp | this error scenarios beg for a goddamn monad. |
| 01:20 | algal | bbloom: been browsing through your archives, especially your old Rails stuff from 2010. Curious if you have any favorites for CLojure-based webdev, or if you think there's nothing competitive. |
| 01:20 | bitemyapp | there are a lot of people here happily doing Clojure web dev. |
| 01:21 | bitemyapp | you might ask if any of them have a setup that works for them instead of seeking a negative result. |
| 01:21 | algal | bitemyapp: what are most of them using, is my question. Just building directly on top of ring/compojure? or ... ? |
| 01:21 | bitemyapp | algal: ring/compojure is the most popular. a good "getting started" setup for that is Luminus. http://www.luminusweb.net/ |
| 01:27 | algal | bitemyapp: Not seeking a negative result, but I am interested in knowledgable comparison. My impression is Rails is a lot more mature for webdev stuff but I've never actually used it. |
| 01:27 | algal | But I can see how it's a sensitive topic so maybe I'll just shut up. :) |
| 01:28 | bitemyapp | algal: it's not a sensitive topic, just trying to provide methodological guidance. |
| 01:28 | bitemyapp | algal: Clojure's current web stack lends itself very well to data heavy projects. |
| 01:28 | bitemyapp | stuff that is mostly samey-samey web forms and the like Rails might be more productive for, but there's a lot of opportunity to abstract stuff like that away in Clojure - partly due to being a Lisp. |
| 01:30 | algal | bitemyapp: by data-heavy do you mean a web service that does a lot of actual computation, or do you mean stuff that handles a lot of data in the browser? |
| 01:32 | bitemyapp | algal: both, anything where you've got a lot of cross-talk between server and client too. |
| 01:32 | justin_smith | algal: I think data heave means large amount of data being summarized or operated on in some way |
| 01:33 | bitemyapp | algal: yes, what justin_smith said. |
| 01:34 | algal | ok. thanks for the perspective. |
| 01:38 | bitemyapp | sweet, my error handling works. |
| 01:39 | justin_smith | bitemyapp: nice, how did you work it out? |
| 01:39 | bitemyapp | 勝! |
| 01:40 | bitemyapp | justin_smith: there's a finalizer message that gets sent to the agent, that finalizer nukes the promises (delivers exception) and then the agent itself (reraises) |
| 01:41 | bitemyapp | the "run" function throws if the promise is an instance of java.lang.Exceptio |
| 01:41 | bitemyapp | and I'm adding an "agent dead?" check as well. |
| 01:41 | TEttinger | bitemyapp, if I order that at a chinese restaurant, will i get kicked out? |
| 01:41 | bitemyapp | there's a possibility for a race condition here but I've got a timeout and a secondary check that I'm adding. |
| 01:41 | justin_smith | you would think there would be something more concise for such a common thing |
| 01:41 | bitemyapp | TEttinger: it's katsu, the japanese for victory. So...I don't know? |
| 01:42 | bitemyapp | justin_smith: hum. yeah there's no good way to do this, mostly because people use sockets in a synchronous way usually. |
| 01:42 | TEttinger | ah, I saw many little strokes and thought chinese |
| 01:42 | bitemyapp | justin_smith: so its expected your synchronous reader/writer will have knowledge of direct causality. |
| 01:42 | justin_smith | TEttinger http://en.wiktionary.org/wiki/%E5%8B%9D |
| 01:42 | bitemyapp | justin_smith: the way I designed revise's conn mgmt uses sockets MUCH more efficiently but there's some non-determinism here. |
| 01:42 | TEttinger | I have had chicken katsu now that I think about it |
| 01:43 | bitemyapp | TEttinger: that's a japanese dish though :) |
| 01:43 | TEttinger | indeed |
| 01:43 | bitemyapp | the timeout + re-check for error handles the race condition scenario but I'm not totally happy with it even if the likelihood of encountering it is minimal. |
| 01:44 | bitemyapp | I guess this comes with the territory of async + using socket resources in a semi-novel way. |
| 01:44 | justin_smith | TEttinger: found that via wiktionary, via a google search, seems it is victory in han and kanji |
| 01:44 | TEttinger | it looks like a lot of strokes |
| 01:44 | justin_smith | we just need an immutable and atomic network layer |
| 01:44 | justin_smith | and these concerns are obviated |
| 01:45 | algal | bbloom: also liking this one on ORMs and declarative schemas. |
| 01:46 | justin_smith | TEttinger: any golfer will tell you that victory comes after many strokes, I just copied and pasted the character into my search bar in my browser |
| 01:46 | TEttinger | haha |
| 01:49 | bitemyapp | `cbp: around? |
| 01:53 | `cbp | bitemyapp: kinda what's up |
| 01:53 | `cbp | bitemyapp: oh you got the error handling down? nice |
| 01:54 | jared314 | when connecting datomic to a heroku postgres instance, how do you add the NonValidatingFactory? |
| 01:54 | jared314 | i tried the db params but it still gives me a cert error |
| 01:57 | bitemyapp | `cbp: yeah it works, but there are more changes to the API |
| 01:57 | bitemyapp | `cbp: breaking ones, unless you object. |
| 01:57 | bitemyapp | `cbp: run is derefs promise with a timeout and checks for errors by default, run-async just returns the promise, no error handling. |
| 01:58 | bitemyapp | `cbp: ordinarily I wouldn't have fucked with run, because I know it's *everywhere*, but the safety really commends the change. |
| 01:58 | `cbp | so the previous run is now run-async? |
| 01:58 | `cbp | and run blocks? |
| 01:58 | bitemyapp | `cbp: yep and yep, but run blocks with a timeout that can be overridden. |
| 01:58 | bitemyapp | `cbp: want me to just push? |
| 01:59 | bitemyapp | this also means query results don't need to be deref'd anymore. |
| 01:59 | `cbp | bitemyapp: yeah just push its not very breaking |
| 02:01 | bitemyapp | `cbp: let me investigate one other thing real quick, to see if I can come up with a cleaner error, then I'll push in a moment. |
| 02:01 | `cbp | ok |
| 02:03 | bitemyapp | `cbp: bja ended up getting hijacked by some important work-stuff, but possibly they can collaborate on a future project with us :) |
| 02:06 | bitemyapp | sweet! my hypothesis was right. |
| 02:07 | bitemyapp | `cbp: pushed! |
| 02:07 | bitemyapp | `cbp: are you willing to fix the tests? |
| 02:07 | bitemyapp | if not, I can get rid of the unnecessary derefs. |
| 02:07 | `cbp | its just 2 :p |
| 02:07 | `cbp | ill be available later |
| 02:08 | bitemyapp | `cbp: okie dokie. I'd like a docs + tests update and a push to clojars soon. Let me know if you want help with anything, I feel a little bad dumping this on you. |
| 02:08 | bitemyapp | `cbp: on the bright side, proper error handling now! |
| 02:08 | bitemyapp | noodling how to handle errors in an asynchronous connection model like this wasn't super-easy :) |
| 02:09 | `cbp | its pretty original |
| 02:09 | `cbp | far as i know |
| 02:11 | bitemyapp | `cbp: I'd hesitate to call it original, but it's definitely uncommon. I'm familiar with a decent number of databases and protocols and I can't think of any that do this. |
| 02:11 | bitemyapp | `cbp: just pushed a last minute addition, reader thread kills itself off if there's an agent error. |
| 02:12 | bitemyapp | `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 | `cbp | huh |
| 02:12 | bitemyapp | I use that to detect socket death / server shutdown at the moment. |
| 02:12 | `cbp | lol |
| 02:12 | bitemyapp | specifically, to make the errors more descriptive. |
| 02:12 | bitemyapp | it handles any source of error I can fathom right now. |
| 02:13 | bitemyapp | when you see the logic in (defn run ...) you'll see how paranoid I'm being. |
| 02:13 | bitemyapp | yogthos: hi! |
| 02:15 | bitemyapp | `cbp: with that, I'm going to play some League of Legends. Ping me if you need anything. |
| 02:18 | `cbp | bitemyapp: should join us at dota sometime :) |
| 02:21 | bitemyapp | `cbp: I play DotA2, I'd love to play with you |
| 02:21 | bitemyapp | `cbp: ping me whenever you'd like to do a game |
| 02:24 | bitemyapp | `cbp: I mostly play league because the games finish faster than DotA2 |
| 02:24 | `cbp | bitemyapp: i play with some friends and we used to lol but were bored of it now |
| 02:24 | bitemyapp | `cbp: well you know where to find me :) |
| 03:10 | muhoo | what does this weird destructure form do? (let [{[foo] :bar} mystery] ..) |
| 03:12 | muhoo | i've seen {:keys [foo] :as bar} many times, but not {[foo] :bar} before |
| 03:13 | noidi | muhoo, {local-name :key} takes the value under :key from the map, and assigns it to local-name. local-name may itself contain further destructurings |
| 03:13 | noidi | so, in your case if the map looks like {:bar [1 2 3]}, the value of foo will be 1 |
| 03:14 | noidi | because of the destructuring (let [[foo] [1 2 3] ...) |
| 03:26 | wei | why doesn't this try/catch block work? https://gist.github.com/yayitswei/7395401 |
| 03:27 | wei | oh-- nevermind, it actually does work! |
| 03:55 | BlackBlock | is there a lib for making partials in general ? (e.g. make partial fn with arbitrary arg positions) |
| 03:58 | bitemyapp | BlackBlock: there's not, and you should avoid that. The general pattern you're seeking is currying. |
| 03:59 | bitemyapp | BlackBlock: currying is something you should look into. core.reducers had a defcurried. Also, you probably want closures. |
| 04:16 | brainproxy | state monad (protocol-monad) + core.async + trampoline = neato |
| 04:18 | danielszmulewicz | Is ritz (the debugger) a dead project? |
| 04:19 | bitemyapp | danielszmulewicz: nope. |
| 04:19 | danielszmulewicz | bitemyapp: Has it been updated to work with cider? |
| 04:21 | danielszmulewicz | Did you guys already update from nrepl.el to cider? |
| 04:22 | danielszmulewicz | Last time I tried (on day 1 of the cider release) there were too many problems and I reverted back to nrepl.el. |
| 04:22 | danielszmulewicz | Things are stable now? |
| 04:23 | ambrosebs | dobry-den: byte array should be (Array Byte) |
| 04:29 | bitemyapp | danielszmulewicz: that's on Cider, not Ritz. |
| 04:29 | bitemyapp | danielszmulewicz: don't use cider if you aren't willing to fix things yourself. |
| 04:31 | danielszmulewicz | bitemyapp: are you using Ritz? |
| 04:33 | bitemyapp | danielszmulewicz: occasionally, yes. |
| 04:34 | danielszmulewicz | bitemyapp: in Emacs? |
| 04:34 | bitemyapp | yes... |
| 04:35 | danielszmulewicz | are you wih nrepl.el or cider? |
| 04:35 | bitemyapp | danielszmulewicz: I'm going to bed momentarily, you have about 2 minutes to ask a useful question. |
| 04:35 | danielszmulewicz | bitemyapp: good night |
| 04:35 | bitemyapp | nrepl.el, because I'm not dumb enough to use cider while it's broken as fuck. |
| 04:35 | danielszmulewicz | bitemyapp: that's what I'm asking |
| 04:36 | danielszmulewicz | bitemyapp: I've seen some enthusiastic praises for cider on the Clojure community on Google+, so I was wondering... |
| 04:36 | bitemyapp | cider is just nrepl.el, but with all the integration broken because shit is half-renamed. |
| 04:37 | bitemyapp | there's nothing to praise. |
| 04:37 | bitemyapp | I have no idea why you would take sycophantic comments on Google+ seriously. |
| 04:37 | bitemyapp | instead of actually looking at what was done |
| 04:38 | danielszmulewicz | bitemyapp: You made your point. You must be tired. Sleep well. |
| 04:46 | bitemyapp | ~cider is Cider is unstable and broken right now, most things don't work. Use the last stable version of nrepl.el for now. |
| 04:46 | clojurebot | You don't have to tell me twice. |
| 04:46 | bitemyapp | ~cider |
| 04:47 | clojurebot | cider is rage-inducing |
| 04:47 | bitemyapp | ~cider |
| 04:47 | clojurebot | cider is rage-inducing |
| 04:47 | bitemyapp | ~cider |
| 04:47 | clojurebot | cider is Cider is unstable and broken right now, most things don't work. Use the last stable version of nrepl.el for now. |
| 04:47 | bitemyapp | very good. |
| 04:47 | bitemyapp | ~cider |
| 04:47 | clojurebot | cider is rage-inducing |
| 04:47 | bitemyapp | lol :) |
| 06:19 | allenj12 | is there an easy way to go from a 2d list to a 1d list |
| 06:23 | allenj12 | nvm got it (apply concat) |
| 07:07 | rrc | I was struggling to determine the cause of a "CompilerException java.lang.NullPointerException, compiling:(form-init3395114284023394383.clj:1:8)" exception I was getting. Ultimately I found nil being passed as an argument to -, but it was only down to tedious commenting out and printlns. Is there are better way to debug these sorts of issues? Why didn't tools.trace work in this case? |
| 07:15 | john2x | curious, is this fn in good form/idiomatic? http://vpaste.net/R8AH5 using an atom for mutation in a for loop. |
| 07:17 | dobry-den | john2x: no |
| 07:18 | john2x | hmm. what would be a better way to achieve what I want? |
| 07:18 | dobry-den | yeah sorry, i pressed enter too soon. |
| 07:19 | dobry-den | first of all, im pretty sure that :let (in the for-loop) is run every iteration |
| 07:19 | dobry-den | so that slurp is being run |
| 07:20 | john2x | yes, that part's intended. it's calling an api request for each `word` |
| 07:20 | dobry-den | oh i see |
| 07:20 | dobry-den | it's an http request |
| 07:20 | dobry-den | haha |
| 07:20 | john2x | yes :) it's an api to a thesaurus |
| 07:21 | dobry-den | the first thing youd want to do is make many requests at once |
| 07:21 | dobry-den | pretty sure that's gonna wait for the full slurp on eeeeevery word |
| 07:23 | dobry-den | Continuing on our naive path, an easy change would be to turn the atom into an agent |
| 07:24 | dobry-den | and use send-off in the loop |
| 07:25 | dobry-den | http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/send-off |
| 07:26 | john2x | ah, I kinda need the fn to be synchronous. :) it's basically to get synonyms of a list of words. |
| 07:26 | dobry-den | does each request have to be synchronous? |
| 07:27 | dobry-den | the idea is that you make all the requests asynchronously, but synchronously wait for them all to complete |
| 07:28 | john2x | nope. ah, I think I follow now. so I'll just realize the agent and then start parsing the responses? |
| 07:29 | dobry-den | yeah. although still not an ideal solution, but it came to mind an incremental improvement |
| 07:30 | dobry-den | you could probably even make a trivial change to use `pmap` and begin processing immediately, but it's more for cpu bound things |
| 07:34 | dobry-den | unfortunately youre not talking to the parallel programming guru here. |
| 07:35 | john2x | thanks. improvement is improvement :) what about the part of using an atom as a variable for keeping track of a mutating object while iterating/looping? |
| 07:36 | dobry-den | like i think a simple solution could be to map a function across your words that returns an async promise/future (dunno which one is which) of the request, then lazily consume them with pmap |
| 07:36 | Morgawr | is there a reason why "send" and "send-off" functions in clojure don't end with a '!' ? It feels a bit inconsistent because, as far as I understand, they have side-effects and are not that different from swap! or reset! or similars |
| 07:37 | dobry-den | john2x: in general you want to use loop/recur and update the loop binding when it's sequential/synchronous stuff |
| 07:38 | dobry-den | but of course here we'd wanna coordinate work across threads |
| 07:38 | hyPiRion | Morgawr: Probably same as why alter, commute, ref-set and ensure doesn't end with an '!': Different semantics within a transaction I guess. |
| 07:38 | dobry-den | john2x: also, iirc, the swapping an atom can actually run the function multiple times |
| 07:39 | dobry-den | it does something called compare-and-set |
| 07:40 | dobry-den | since you dont actually care what order the requests conjed to the ref, atom doesnt make sense |
| 07:40 | Morgawr | hyPiRion: but agents don't have to run inside a transaction though |
| 07:40 | hyPiRion | I know |
| 07:40 | Morgawr | but yeah, just wanted to know if there was any explicit reasoning behind this or if it's just an occurrence |
| 07:40 | dobry-den | now u got 'im shurgin |
| 07:40 | hyPiRion | *shrug, heh |
| 07:40 | Morgawr | lol |
| 07:41 | dobry-den | Morgawr: Only Hickey Knows |
| 07:41 | hyPiRion | It's probably not too far off from what you said, dobry-den, something about no retries |
| 07:42 | dobry-den | i worked in a ruby codebase where one contractor before me used the bang! to mark destruction. of course, it's a monolithic rails app. why doesnt every method have a bang on it? only the code he wrote had them. |
| 07:42 | dobry-den | so every day was this constant game of trying to remember which functions had the bang |
| 07:43 | hyPiRion | haha |
| 07:43 | dobry-den | sort of like being stuck in a hell where you're trying to plug in usb drives the right way every time |
| 07:44 | Morgawr | damn 4-dimensional usb sticks |
| 07:45 | hyPiRion | I wish they were something like a CAT cable with a little bump up on one side |
| 07:45 | dobry-den | i wish it was like, a triangle |
| 07:45 | dobry-den | er wait |
| 07:45 | dobry-den | why not just a circle |
| 07:45 | hyPiRion | yeah, hah |
| 07:45 | Morgawr | I'd just take it like a normal audio jack |
| 07:45 | Morgawr | just plug it in and forget about it |
| 07:45 | dobry-den | that's not how you make money |
| 07:46 | Morgawr | is that why I'm poor? :( |
| 07:46 | dobry-den | :( |
| 07:46 | john2x | or PS/2. now that was hard to get right |
| 07:46 | dobry-den | haha |
| 07:46 | dobry-den | on the dark side of a computer buried on the underside of a desk, yeah |
| 07:50 | dobry-den | john2x: i often use http://http-kit.org/client.html for http requests which makes async stuff easy |
| 07:52 | john2x | dobry-den: thanks for help. :) I'll finish the rest of my project first then go back to improving. |
| 07:53 | dobry-den | yeah that's a trait i would like to have |
| 07:54 | dobry-den | but the yaks arent gonna shave themselves, i tell myself |
| 07:55 | john2x | and yet here I am asking about whether a fn was idiomatic with still a lot left to finish ;P |
| 08:05 | dobry-den | it would be nice if clojure had a batteries-included way to consume the first N elements of async jobs. |
| 08:07 | dobry-den | basically, a pmap for i/o bound jobs |
| 08:09 | dobry-den | the problem with pmap for i/o bound work (and work in general, really), is that you have no control over how many items it's realizing at once, |
| 08:10 | dobry-den | and, worst of all, even though it will realize [a b c d e ...] at once, if b c d e take 1 second but a takes 30 seconds, it will wait 30 seconds before consuming any more elements |
| 08:11 | dobry-den | i'm gonna write it right now ^_^ |
| 08:11 | dobry-den | pooled-map |
| 08:14 | dobry-den | dont try and stop me cuz u cant |
| 08:45 | opqdonut | dobry-den: seque does almost that |
| 08:46 | opqdonut | dobry-den: We have a custom version of pmap that takes an ExecutorService (i.e. thread pool) |
| 08:46 | opqdonut | ... which is useful |
| 09:23 | CookedGr1phon | Hi, I'm doing some stuff which requires a few small bytebuffers, and it's really annoying that their tostring gives me useless info like #HeapByteBuffer [lim=0, size=16, ...] |
| 09:24 | CookedGr1phon | so what I really want to do is override the tostring, but I'm creating it from a static allocate method |
| 09:24 | CookedGr1phon | and it's all naff and convoluted |
| 09:24 | CookedGr1phon | is there any other way to override the str output to give me a useful dump instead? |
| 09:24 | CookedGr1phon | or am i stuck going down this route |
| 09:24 | CookedGr1phon | (hoping someone's going to tell me about a handy to-string protocol or something) |
| 09:35 | CookedGr1phon | what is it that appends the #<Classname ... > part of the output? |
| 10:22 | noidi | CookedGr1phon, you can't customize str output, but you can define a custom print-method http://clojuredocs.org/clojure_core/clojure.core/print-method |
| 10:23 | noidi | and then use pr-str to create a string representation of your buffers |
| 10:26 | CookedGr1phon | genius!!! Thankyou so much |
| 10:26 | CookedGr1phon | that works perfectly |
| 10:27 | CookedGr1phon | exactly what I was looking for, all the stuff I care about was using pr-str under the hood anyway (i.e. test output etc) |
| 10:29 | CookedGr1phon | this is infinitely more readable |
| 10:31 | justin_smith | what about print-dup? |
| 10:32 | justin_smith | I thought pr-str used print-dup |
| 10:33 | justin_smith | with *print-dup* true (which indicates you are trying to write a readable version of your thing), it uses print-dup instead of print-method |
| 10:35 | justin_smith | (the chain of calls going down being pr-str -> pr -> clojure.core/pr-on |
| 10:36 | justin_smith | ) |
| 10:38 | CookedGr1phon | what practical difference does this make? when is pr-str meant to be used vs print-dup, i can't really tell the difference from the docs |
| 10:39 | CookedGr1phon | other than print-dup only does it when you tell it to use that representation specifically? |
| 10:40 | justin_smith | pr-str is meant to be readable |
| 10:40 | justin_smith | err |
| 10:41 | justin_smith | print dup is called by pr-str |
| 10:41 | CookedGr1phon | and print-dup is meant for serialisation? |
| 10:41 | justin_smith | if you set the *print-dup* special |
| 10:41 | justin_smith | otherwise it calls print-method |
| 10:41 | justin_smith | the first is if you want something that is loadable from a string to get the same thing back |
| 10:42 | justin_smith | the latter is meant to be readable |
| 10:42 | CookedGr1phon | okay, I only care about looking at this, i'm not trying to serialize, there's much better serializations for this data |
| 10:42 | justin_smith | check out (source clojure.core/pr-on), which pr-str ends up calling a couple of levels down |
| 10:42 | justin_smith | yeah, pr-str calls it if you set that special |
| 10:43 | justin_smith | but if all you care about is readable output, setting print-method should suffice |
| 10:44 | CookedGr1phon | cool |
| 10:44 | CookedGr1phon | thanks for the clarification |
| 11:06 | CoconutCrab | free |
| 11:06 | CoconutCrab | ops, sorry, wrong window |
| 11:14 | justin_smith | https://www.refheap.com/20657 any hints why this doesn't print anything but a blank line? |
| 11:15 | justin_smith | updated to show a call that should output but does not |
| 11:16 | justin_smith | NEVER MIND IT'S LAZY AND IM DUM |
| 13:12 | sritchie | any liberator users? |
| 13:12 | sritchie | I'm trying to return a 404 not found page for a resource, |
| 13:12 | sritchie | for js, css or img |
| 13:14 | `cbp | bitemyapp: ok i added some docs and fixed the tests and deployed 0.0.4 |
| 13:16 | `cbp | bitemyapp: let me know if you want me to add something else to the docs |
| 13:17 | edoloughlin | Anyone using cider? I have a repl buffer and a *cider-error* buffer but there's nowhere I can see my stdout (i.e., my log output) |
| 13:17 | danielszmulewicz | sritchie: liberator user here |
| 13:18 | sritchie | I think I just found my answer: liberator.representation/ring-response |
| 13:18 | sritchie | for returning a different content type from, say, a js request |
| 13:18 | danielszmulewicz | sounds about right |
| 13:18 | danielszmulewicz | indeed |
| 13:18 | sritchie | the other Q I had |
| 13:19 | sritchie | was about how to stack behaviors |
| 13:19 | sritchie | say I want to share authorization logic, |
| 13:19 | sritchie | but have everything else be different |
| 13:20 | danielszmulewicz | liberator has :authorized? which you put everywhere where you need to authorize a resource |
| 13:21 | danielszmulewicz | Does that address the question? |
| 13:21 | sritchie | well, yeah - |
| 13:21 | danielszmulewicz | OK, cool |
| 13:21 | sritchie | but is the pattern to make a function taht returns a resource? |
| 13:21 | sritchie | (defn get-resource [auth-fn] (resource :authorized? auth-fn ….)) |
| 13:22 | sritchie | danielszmulewicz: or is there a way to compose the two |
| 13:22 | danielszmulewicz | no, the pattern is to write a function to return a boolean |
| 13:22 | danielszmulewicz | *that* |
| 13:23 | sritchie | danielszmulewicz: yeah, for authorization |
| 13:23 | danielszmulewicz | sritchie http://clojure-liberator.github.io/liberator/doc/decisions.html |
| 13:24 | sritchie | the question is how to nest them |
| 13:24 | sritchie | for example, say I have the same handle-not-found logic for every one of my resources |
| 13:24 | sritchie | since these are all constructed like maps, |
| 13:24 | sritchie | it would make sense to be able to merge them, for example |
| 13:24 | danielszmulewicz | you don't nest anything, that's the beauty of liberator |
| 13:24 | danielszmulewicz | you have a decision graph |
| 13:25 | sritchie | but you duplicate quite a bit if you have 10 resources with the same handle-not-found fn |
| 13:25 | danielszmulewicz | anywhere in the chain you can change the outcome of the request |
| 13:25 | sritchie | yup, got that part, it's really awesome |
| 13:25 | sritchie | what I want to do is lock down a bunch of those decision points, |
| 13:25 | danielszmulewicz | if you have |
| 13:25 | sritchie | and then write resources that fill in the rest |
| 13:25 | sritchie | same auth, same :handle-not-found |
| 13:26 | danielszmulewicz | you reuse the same function |
| 13:26 | danielszmulewicz | that's not called duplication |
| 13:27 | sritchie | but every resource has :handle-not-found handler |
| 13:27 | sritchie | when you could imagine something like |
| 13:27 | sritchie | (defresource my-resource :base-resource something-else :handle-ok ....) |
| 13:27 | sritchie | like, a base-resource key |
| 13:28 | sritchie | danielszmulewicz: and then you'd just merge the two maps to create the final resource |
| 13:28 | sritchie | if you've got 10 decision points that you want to share for every resource, |
| 13:28 | danielszmulewicz | I think you can do something like that with the routing but I don't know exactly how |
| 13:28 | sritchie | I would argue that it's not clean to have to copy-paste those ten lines |
| 13:28 | sritchie | those 10 key-function pairs |
| 13:28 | sritchie | okay, liberator routing? |
| 13:29 | danielszmulewicz | it's the compojure part where you can generalize routes |
| 13:29 | amacdougall | Here's a new one -- I've been using {:optimizations :whitespace} for ClojureScript dev, but when I switch it to :none, I get "goog is not defined" at the very start of my main.js. The exact line that causes it is: goog.addDependency("base.js", ['goog'], []); |
| 13:29 | amacdougall | Anyone know what's up? |
| 13:29 | amacdougall | I'm on ClojureScript 2030. |
| 13:29 | sritchie | oh, then internally start switching again, in handle-okay, using compojure? |
| 13:29 | sritchie | yeah, I guess you could use a resource as a middleware |
| 13:29 | sritchie | interesting, that would work |
| 13:29 | danielszmulewicz | yes, liberator is ring compliant and I think you can sudy compojure addanced routing |
| 13:29 | danielszmulewicz | *study* |
| 13:30 | sritchie | sweet, I think that's the right call. thanks guys |
| 13:30 | danielszmulewicz | *advanced* |
| 13:30 | sritchie | thanks danielszmulewicz, that is :) |
| 13:30 | danielszmulewicz | sritchie: cool, good luck |
| 13:32 | amacdougall | I have a hunch that since I'm using some Google Closure libraries, my app will only work if it's fed into the Closure Compiler... and I guess specifying no optimizations skips that stage? You'd think there would be some provision for that though. |
| 13:40 | amacdougall | Hm, look like it might be fixed by this commit: https://github.com/clojure/clojurescript/commit/dbf31380b925815d832a6cc53364d063a59dafad |
| 13:56 | rabidsnail | What's the idiomatic way to do argmin? |
| 13:57 | justin_smith | rabidsnail: waht is argmin? |
| 13:57 | justin_smith | n/m wikid it |
| 13:59 | rabidsnail | I mean I could do it with loop/recur, but it seems like there should be a cleaner solution |
| 14:00 | dobry-den | I need to parse "little-endian variable-length integers where the MSB determines the sign of the integer", but for some reason i just can't figure out how to write a function that turns 0x81 into -1, and 0x80 into -0. |
| 14:00 | justin_smith | rabidsnail: I am putting together a version with reduce, assuming args are a sequence to iterate |
| 14:00 | justin_smith | generating that sequence is a separate problem I think |
| 14:01 | rabidsnail | justin_smith: yeah, I'd expect the signature to be (argmin function sequence) |
| 14:02 | justin_smith | (defn argmin [[arg & args] f] (first (reduce (fn [[arg min] arg] (let [v (apply f arg)] (if (< v min) [arg v] [arg min]))) [arg (f arg)] f))) |
| 14:02 | justin_smith | maybe |
| 14:02 | justin_smith | that allows args to be more than one arg, just put each set of args in an array or vector |
| 14:03 | rabidsnail | right, that'd work |
| 14:03 | rabidsnail | I'm kind of surprised there's nothing like that built in |
| 14:04 | rabidsnail | it's a really common thing to want to do when translating math out of papers |
| 14:04 | justin_smith | though I used the name "arg" one too many times |
| 14:08 | justin_smith | rabidsnail: https://www.refheap.com/20677 |
| 14:08 | justin_smith | my go at it |
| 14:09 | justin_smith | if the function is always single argument you can simplify it a bit by taking out the apply usage |
| 14:10 | justin_smith | also the order of arguments should be reversed so the sequence is last |
| 14:10 | justin_smith | that is the normal order for clojure |
| 14:10 | CookedGr1phon | (max-key (comp - f) a b c d ...) |
| 14:11 | CookedGr1phon | oh |
| 14:11 | CookedGr1phon | or min-key |
| 14:11 | CookedGr1phon | that would also work |
| 14:12 | justin_smith | CookedGr1phon: awesome, I did not know about max-key |
| 14:12 | CookedGr1phon | it's a bit weirdly named |
| 14:12 | justin_smith | or min-key! |
| 14:12 | justin_smith | nice |
| 14:12 | justin_smith | TIL |
| 14:13 | arrdem | clojurebot: ping |
| 14:13 | clojurebot | PONG! |
| 14:13 | justin_smith | rabidsnail: so your assumption was correct, it did exist, and it had the most intuitive name :) |
| 14:13 | justin_smith | *second most intuitive |
| 14:14 | rabidsnail | CookedGr1phon: thanks |
| 14:14 | CookedGr1phon | np |
| 14:14 | justin_smith | yeah, why think in terms of keys rather than functions? |
| 14:15 | cYmen | hm... I have created a clojure project using leiningen and am editing the src file with vim including fireplace. How do I trial run my app? |
| 14:15 | justin_smith | also it runs f twice as often as my version does, looking at the source :) |
| 14:15 | cYmen | I'm just trying to learn how to use ring so I suppose I should start a repl and start a server but what is the best way to do that? |
| 14:16 | justin_smith | I like my version better, it calls the function half as many times and allows for multiple arguments to the function :P |
| 14:17 | justin_smith | cYmen: using ring, I usually fire up 'lein ring server' and start an nrepl from inside the ring process |
| 14:18 | justin_smith | that way I can interact with variables inside the process that serves requests (ie. save a request or intermediate value in an atom so I can play with it in the repl) |
| 14:18 | justin_smith | the github page for nrepl shows how to start the server from inside your app, it is straightforward |
| 14:20 | cYmen | justin_smith: So the "lein ring server" part I think I have configured and it seems to work. How do I start an nrepl inside the ring process? |
| 14:22 | justin_smith | cYmen: as I said, it is mentioned on the github page: https://github.com/clojure/tools.nrepl |
| 14:22 | justin_smith | (defonce server (start-server :port 7888)) |
| 14:22 | cYmen | uh yeah already copy pasting stuff |
| 14:22 | justin_smith | you would run that inside the init function of your ring app |
| 14:23 | cYmen | init function... |
| 14:23 | cYmen | *sigh* |
| 14:23 | cYmen | There is so much STUFF to help you get started with clojure and so little explanation of that stuff I feel like I am getting dumber. |
| 14:24 | justin_smith | the init function being the one that is passed as :init to the :ring key in your project.clj |
| 14:24 | justin_smith | and that is where you define it if you don't have one already :) |
| 14:24 | cYmen | apparently I don't |
| 14:25 | justin_smith | (defproject ... :ring {:init your.ns/your-init-f}) |
| 14:25 | justin_smith | that's all you need |
| 14:26 | justin_smith | well the stuff in the ... is assumed to already be in there :) |
| 14:26 | cYmen | Yeah, I got that part. :p |
| 14:26 | justin_smith | cYmen: there is a bootstrapping period, after which the help all starts to make sense |
| 14:26 | justin_smith | in my experience |
| 14:27 | justin_smith | what I am saying is if you stick to it you will get there |
| 14:27 | justin_smith | and feel free to ask clarifying questions if my help isn't helpful |
| 14:28 | justin_smith | I can share a project.clj of mine if you want to see what I mean regarding how the :init fn is defined |
| 14:28 | cYmen | I should be able to get that right, let me check. ;) |
| 14:30 | justin_smith | once you have the init function set up, and you start the nrepl server in that, you should just be able to connect to that port with fireplace |
| 14:30 | cYmen | So ....uh if I did it right how do I know? |
| 14:33 | justin_smith | it will open the port, you can connect to it with 'lein repl :connect <port>' in a terminal |
| 14:33 | justin_smith | if that works, next step is making fireplace connect to that port |
| 14:34 | yedi | anyone know of langs where 0 is falsy and "" is not? (or vice versa) |
| 14:34 | justin_smith | so if you pasted that code, 'lein repl :connect 7888' |
| 14:35 | xuser | yedi: C ? |
| 14:36 | cYmen | justin_smith: It keeps telling me "Port is required"' |
| 14:37 | swarthy | cYmen: if you don't mind spending a bit of money 'Web Development in Clojure' by yogthos (in this channel) is really good at explaining all this stuff. |
| 14:38 | justin_smith | xuser: I think "" is a NULL pointer in C, which is usually 0, which is falsey |
| 14:38 | mtp | justin_smith‘ the empty string is not a null pointer |
| 14:38 | justin_smith | the C spec says any value can be NULL for a specific platform, but 0 is normal |
| 14:38 | justin_smith | BUT |
| 14:39 | justin_smith | cYmen: lein repl :connect 7888 |
| 14:39 | justin_smith | or whatever port number you told the server to open on |
| 14:40 | cYmen | justin_smith: I think my irssi just crashed, sorry. |
| 14:40 | cYmen | justin_smith: LEIN_REPL_PORT=7888 lein repl :connect works. See https://github.com/technomancy/leiningen/issues/1344 |
| 14:40 | amacdougall | bja just helped me out big time with my ClojureScript issues -- thanks! |
| 14:41 | justin_smith | mtp: what about an empty string const literal? |
| 14:42 | justin_smith | that would be the value 0 no? |
| 14:42 | mtp | justin_smith‘ still not a null pointer |
| 14:42 | mtp | that gets allocated in rodata iirc |
| 14:42 | justin_smith | though the address of that string is not likely to be 0, it will have 0 as its first value |
| 14:42 | mtp | exactly |
| 14:43 | mtp | the name of the thing, is not the thing |
| 14:43 | justin_smith | which is the value you get if you deref that location |
| 14:43 | mtp | but that still isn't a null pointer! |
| 14:43 | mtp | if you deref it, you get (char)0 |
| 14:44 | mtp | a null pointer is (char *)0 |
| 14:44 | justin_smith | cYmen: oh yeah, I have been avoiding upgrading my lein :) |
| 14:44 | xuser | justin_smith: https://www.refheap.com/20680 |
| 14:45 | justin_smith | mtp: implementation wise the first value you read when you read something that is an empty string will be a 0 |
| 14:46 | mtp | justin_smith‘ but according to the type system, that is still not a pointer to null |
| 14:46 | mtp | it is a null byte |
| 14:46 | cYmen | justin_smith: So how do I connect to that using fireplace? I can't seem to do it with :Connect. |
| 14:46 | justin_smith | yeah, I was wrong about the NULL thing, that is also usually 0, but beside the point |
| 14:46 | justin_smith | yeah |
| 14:47 | justin_smith | indeed |
| 14:48 | justin_smith | mtp: as I said above "though its address won't be 0, its value when you deref will be" |
| 14:48 | mtp | justin_smith‘ it will be a literal 0 byte, which, as you mentioned, may not be the representation of a null pointer on your platform |
| 14:48 | mtp | (void *)0 is not ever guaranteed to be (char)0 |
| 14:49 | mtp | (it *frequently is*, on today's machines) |
| 14:50 | rabidsnail | Is there a way to say "here is a value c which is the same as value a except use value b for comparison purposes" (where a is, say, a custom hash)? |
| 14:50 | rabidsnail | like, is there a protocol for comparisons? |
| 14:51 | justin_smith | xuser: fair enough, this is the version that does what I was thinking: https://www.refheap.com/20681 which of course is not standard C at all |
| 14:51 | mtp | imagine a machine where pointers are themselves typed - a null pointer could be the bit pattern 0x01 in memory, but C guarantees that when you type (void*)0 you get a null pointer. |
| 14:51 | mtp | :) |
| 14:51 | justin_smith | mtp: yeah, I said a couple times I was totally wrong about that null thing already |
| 14:52 | mtp | tl;dr there are a lot of subtleties here |
| 14:52 | justin_smith | agreed |
| 14:52 | mtp | http://thoughtmesh.net/publish/367.php |
| 14:52 | justin_smith | I only briefly implied otherwise, and have since corrected myself |
| 14:53 | cYmen | justin_smith: Actually..when I have that repl connected how can I interact with the ring server and do stuff like setting a var, redefining a function or reloading the source? |
| 14:55 | justin_smith | mtp I actually wouldn't be surprised if that existed somewhere |
| 14:56 | mtp | justin_smith‘ yeah, it is perfectly legal C, but it breaks the assumption that (char)0 == (void *)0 :) |
| 14:57 | mtp | (upon which we now agree) |
| 14:58 | justin_smith | cYmen: simple example: in an ns which serves the request, do (defonce requests (atom [])), then in the context where you use the requests (swap! requests conj request) - this is all done in your source. Next, from the repl you can access the requests (deref your.ns/requests) and access values in them etc. |
| 14:59 | justin_smith | or, in the repl, you can switch to one of your existing namespaces (ns your.ns) and redefine functions with defn, or call those functions, etc. |
| 15:00 | justin_smith | mtp: fascinating article |
| 15:01 | xuser | justin_smith: your example is valid C but you are checking against an element of the array (the null character), if my example we are checking against a pointer (address). |
| 15:03 | xuser | s/if/in/ |
| 15:05 | justin_smith | xuser: true |
| 15:05 | justin_smith | it was a last ditch attempt to present that some aspect of what I was saying was not complete bullshit :) |
| 15:06 | justin_smith | not that I got out of it with any dignity intact |
| 15:06 | xuser | ;) |
| 15:08 | justin_smith | mtp: "In fact, since punning always involves the representation and not the semantics, it is only truly possible outside of the defined semantics of a given language. In most languages, what this means is that type punning is simply not possible." |
| 15:08 | mtp | justin_smith‘ do you agree or disagree? |
| 15:09 | justin_smith | mtp: I have, for a personal project, taken Doubles, put them in a byte array, then extracted longs out of the byte array |
| 15:09 | justin_smith | I wonder if that is a counter example to what he is saying? clearly I was type punning, if in a roundabout way |
| 15:09 | mtp | that is an explicit operation that converts your doubles into C's native memory model, and then out again |
| 15:10 | mtp | so you were type-standup-comedying. :) |
| 15:10 | justin_smith | (this was done because I wanted to get the set / unset bits in the doubles, to debug deserialization from a binary file generated by a C program) |
| 15:13 | justin_smith | that it is not possible in most languages? I would say rather that it is not easy in most languages. |
| 15:14 | mtp | it requires two conversions |
| 15:14 | mtp | instead of directly accessing the representation |
| 15:14 | justin_smith | hah |
| 15:14 | mtp | this is the point of the article! |
| 15:14 | justin_smith | yeah, there are intermediate steps in my clojure version that don't need to happen in c |
| 15:14 | mtp | to get this behavior out of most languages, you have to do work to get things into a bytearray |
| 15:15 | mtp | c's memory model IS a byte array |
| 15:16 | justin_smith | mtp: it is really really weird to see Derrida referenced in a paper about C |
| 15:19 | justin_smith | OK |
| 15:19 | mtp | :) |
| 15:21 | cYmen | justin_smith: How do I redefine them all by re-reading the source after I have made changes? |
| 15:29 | justin_smith | cYmen: (require 'your.ns :reload) |
| 15:30 | justin_smith | from the repl |
| 15:30 | cYmen | I ran this http://pastebin.com/pMBEREVJ with lein ring server-headless and then redefined handler2 in a connected repl. Why doesn't it change the output when I reload the site? |
| 15:31 | cYmen | justin_smith: Thanks, that is super useful. |
| 15:35 | justin_smith | cYmen: because ring is likely not looking up the value of handler on each request |
| 15:35 | justin_smith | there is a trick using #'handler which forces it to resolve it on each new request |
| 15:36 | cYmen | hm |
| 15:36 | justin_smith | also there is middleware like wrap-reload that automatically reloads any changed files on each request |
| 15:36 | justin_smith | cYmen: don't say that yet, I am still forgetting exactly where you specify #'handler instead of handler - I have a framework I use that does a bunch of these things for me |
| 15:37 | justin_smith | I only know the details because I helped write the framework |
| 15:37 | cYmen | I think I just want to reload the entire file every time. |
| 15:38 | cYmen | Coding in the repl isn't pleasant anyway. |
| 15:50 | justin_smith | cYmen: it can be with proper editor integration |
| 15:50 | justin_smith | but yeah, there is the wrap-reload handler if you want that |
| 15:51 | cYmen | I'm actually fine with manually reloading if it is just two keypresses in the repl. |
| 15:51 | justin_smith | http://clojuredocs.org/ring/ring.middleware.reload/wrap-reload |
| 15:56 | justin_smith | uparrow / return |
| 16:10 | TheLastProject | Hey everone. I'm trying my hand at Clojure, and this is my first day playing with it, but I'm running into an issue. My idea was to have the user-input call similarily-named functions. If the user entered "list upper" it should call "list-clothing" with parameter "upper". I've been playing with this for hours, but I can't get it working. Here is my code: http://pastebin.com/sMapEQ5C. The error I get is |
| 16:10 | TheLastProject | "clojure.lang.PersistentList$EmptyList cannot be cast to clojure.lang.Symbol" if I use no parameters and "clojure.lang.PersistentVector$ChunkedSeq cannot be cast to clojure.lang.Symbol" if I use a parameter. The problem is in the last line, at the (rest ...) part, it seems. I was wondering if anyone could figure out what I was doing wrong? |
| 16:16 | justin_smith | TheLastProject: why does add-clothing execute y as a function? |
| 16:18 | TheLastProject | justin_smith: I thought that was supposed to add y to the x vector, although I must admit that I haven't tested add-clothing yet, I wrote it but went to write the resolving of user input before giving it a try |
| 16:19 | justin_smith | also, your call to into for add-clothing does not change your clothing list, it only returns a copied and modified version of it that you don't do anything with |
| 16:19 | TheLastProject | justin_smith: Ah, that is what the whole "unmutable" deal was about. Okay, good to know that needs fixing too |
| 16:23 | justin_smith | TheLastProject: vectors are not mutible |
| 16:23 | justin_smith | you can return a modified version, and replace a binding with that value |
| 16:23 | justin_smith | but the vector itself never changes |
| 16:24 | TheLastProject | justin_smith: Yeah, that's probably what I need to do to get add-clothing to work properly. Still wonder how to get the (resolve ...) part working, hmm... |
| 16:24 | justin_smith | I am working on a slightly easier to debug version of the last part of the code |
| 16:24 | TheLastProject | Ah, okay |
| 16:24 | justin_smith | example: |
| 16:25 | TheLastProject | I've tried literally everything there, heh... |
| 16:25 | justin_smith | ,(let [x [1 2 3] y (into x [2])] [x y]) |
| 16:26 | clojurebot | [[1 2 3] [1 2 3 2]] |
| 16:26 | justin_smith | notice that x is unchanged, but the new binding, y, shows the result (once clojurebot responds) |
| 16:26 | TheLastProject | So for add-clothing I need to overwrite the old x with the new y |
| 16:30 | TheLastProject | justin_smith: Someone else managed to figure out the last line. I had to get the (rest) part outside of the resolve, to call the function with the argument |
| 16:30 | TheLastProject | At very least, thank you a lot for the heads-up on the add-clothing part, that would've really caused a headache otherwise |
| 16:30 | coventry | TheLastProject: clojure actually makes it fairly awkward to overwrite variables like that, because it discourages use of mutable state. If you want to do that, you should probably put x in an atom. |
| 16:30 | TheLastProject | Wait, no |
| 16:31 | justin_smith | TheLastProject: are you running the code from the same namespace where it is defined? if not the resolve will fail |
| 16:31 | TheLastProject | The resolve didn't work >.< |
| 16:31 | TheLastProject | justin_smith: I have no clue how namespaces work, honestly |
| 16:31 | justin_smith | took him long enough! |
| 16:32 | TheLastProject | coventry: Atom. Need to read up on that. Thanks |
| 16:32 | OtherRaven | probably should read up on namespaces too :p |
| 16:33 | TheLastProject | OtherRaven: Heh, obviously, although I've tried that before back in my Python days and never managed to grasp the concept, so I'm not very hopeful about being able to grasp it this time, but we'll see |
| 16:33 | coventry | TheLastProject: I would drop the attempt to resolve the fn vars, and use a dispatch map (or multimethod, if you want another new concept for your pile.) |
| 16:33 | TheLastProject | Well, okay, it are STILL my Python days |
| 16:33 | justin_smith | not with the new y |
| 16:33 | justin_smith | you need to put the version with y added where it was |
| 16:33 | justin_smith | atoms are a standard way to do that |
| 16:34 | OtherRaven | TheLastProject: do you know how java classes work? 'cause namespaces compile down to java classes |
| 16:34 | justin_smith | (swap! x conj y) |
| 16:34 | amalloy | OtherRaven: no they don't |
| 16:34 | justin_smith | ,(let [x (atom [0 1 2] y 3)] (swap! x conj y) @x) |
| 16:34 | clojurebot | #<CompilerException java.lang.RuntimeException: Unable to resolve symbol: y in this context, compiling:(NO_SOURCE_PATH:0:0)> |
| 16:34 | OtherRaven | amalloy: really? I thought they did |
| 16:34 | TheLastProject | coventry: "dispatch map". I have a dispatch function in one of my Python applications, but no clue how that would translate to a "dispatch map" in Clojure, hmm... |
| 16:35 | justin_smith | some day clojurebot will show us that this actually updates x |
| 16:35 | TheLastProject | Would I make a map called "dispatch" which would have things like ":add add-clothing"? |
| 16:36 | coventry | TheLastProject: I.e., (def commands {"add" add-clothing "count" count-clothing}) ((commands cmd) args) |
| 16:36 | TheLastProject | coventry: Ah! Of course1 |
| 16:36 | justin_smith | coventry: yeah I just showed him how to use an atom |
| 16:37 | justin_smith | or tried to |
| 16:37 | justin_smith | every top level binding (created by def or defn etc.) is in a namespace |
| 16:38 | coventry | justin_smith: Yeah I think between us we've covered the two biggest issues. |
| 16:38 | justin_smith | if you don't specify a namespace, clojure looks inside the current binding block (like let or function args), then the current namespace, then the ones you are using |
| 16:39 | justin_smith | yeah, coventry's idea is good |
| 16:39 | amalloy | OtherRaven: i mean, kinda? each namespace does eventually include init code, which is run when the namespace is loaded. but i wouldn't say that the class containing that code *is* the namespace |
| 16:40 | justin_smith | (get {"list" list-clothing "count" count-clothing} (first input-words)) |
| 16:40 | justin_smith | where input-words is a local binding for splitting the input |
| 16:41 | justin_smith | wow clojurebot is slogging |
| 16:41 | addisaden | you need to write like this |
| 16:41 | addisaden | ,(+ 1 2) |
| 16:41 | clojurebot | 3 |
| 16:41 | addisaden | ;) |
| 16:41 | justin_smith | TheLastProject: I just showed you how it is done in the whole (get {...}...) thing above |
| 16:42 | addisaden | ,(get {"list" list-clothing "count" count-clothing} (first input-words)) |
| 16:42 | clojurebot | #<CompilerException java.lang.RuntimeException: Unable to resolve symbol: list-clothing in this context, compiling:(NO_SOURCE_PATH:0:0)> |
| 16:42 | OtherRaven | amalloy: Fair enough. I knew there was a strong correlation between the two concepts, anyway. XD |
| 16:42 | TheLastProject | justin_smith: Yeah, I'm trying to use that, I kinda want to define it somewhere else to keep that line from getting huge. Can I ask you if I end up not getting it to work again? :x |
| 16:45 | justin_smith | ,(future "time-travel high five") |
| 16:45 | clojurebot | #<SecurityException java.lang.SecurityException: no threads please> |
| 16:47 | justin_smith | ,@(future "dereferenced time-travel high five") |
| 16:47 | clojurebot | #<SecurityException java.lang.SecurityException: no threads please> |
| 16:47 | justin_smith | addisaden: I did |
| 16:48 | justin_smith | addisaden: that was not an attempt to use clojurebot, it had symbols that were never defined for him |
| 16:49 | justin_smith | my other attempts to use clojurebot lag a lot, for some reason |
| 16:49 | justin_smith | maybe he thinks I am overusing his services |
| 16:50 | justin_smith | TheLastProject: sure - remember you can build up definitions in the let block :) |
| 16:50 | justin_smith | ,"Eventually" |
| 16:50 | clojurebot | "Eventually" |
| 16:52 | justin_smith | see! that was my ironic call to future above |
| 16:53 | TheLastProject | http://pastebin.com/2Se5qJ47 <- I added a dispatch feature, and it at least calls them correctly, but it doesn't seem to send the arguments correctly. For example, typing "list upper" just returns "upper" and "count upper" returns "You have 1 pieces of clothing defined in collection (upper)". Seems it sends it as "(upper)" instead of "upper", so I guess I need to join the arguments somehow, but (join " " |
| 16:53 | TheLastProject | (rest...)) returns every letter of upper on its own line in "list upper" |
| 16:54 | TheLastProject | I'm probably missing something really simple which I just didn't copy over correctly from what you two said >.< |
| 16:58 | amalloy | TheLastProject: you want (apply (dispatch ...) (rest ...)) |
| 17:00 | amalloy | apply takes a function and a list of arguments, and calls that function with the arguments "unwrapped" |
| 17:01 | TheLastProject | amalloy: The apply unfortunately seems to just split the rest part into one argument per letter. With apply, "list upper" returns every letter of "upper" on a new line. Hmm... |
| 17:02 | amalloy | mmm. then i guess i didn't read something carefully enough |
| 17:02 | TheLastProject | I wonder what exactly is going wrong here. Is "upper" a string, maybe? It should refer to the "upper" variable, so maybe that's wrong |
| 17:03 | TheLastProject | Heh, I ought to stop trying to learn a new language in the last few hours I have in a day, debugging such issues as a new user takes too much time |
| 17:04 | OtherRaven | TheLastProject: that's what insomnia is for |
| 17:04 | TheLastProject | OtherRaven: I don't know if I can afford that, though. I'm already terribly tired every day |
| 17:04 | justin_smith | amalloy: does he? list-clothing expects a list as input |
| 17:06 | TheLastProject | justin_smith: I hope it's clear that I want to have it return the upper list defined just above it if I call it with "upper", just to make sure, because I'm starting to get too confused to read my own code >.< |
| 17:06 | justin_smith | though this will not work for add-clothing |
| 17:06 | justin_smith | but add-clothing is still off, because upper is not an atom |
| 17:07 | coventry | I wish things like read-line played nicer with nrepl.el. |
| 17:07 | justin_smith | (assuming upper is where you wanted to store the accumulated added clothing) |
| 17:07 | TheLastProject | justin_smith: "Because upper is not an atom" Hmm, I really need to figure out what I'm messing up now |
| 17:07 | TheLastProject | Yeah, I do |
| 17:07 | TheLastProject | I think I misunderstood you before |
| 17:07 | OtherRaven | TheLastProject: in that case, probably not a good idea to code before bed. I always have a horrible time sleeping if I have bugs I haven't solved |
| 17:07 | coventry | TheLastProject: http://clojure.org/atoms |
| 17:08 | justin_smith | amalloy: yes, and list-clothing takes one arg, a list |
| 17:08 | justin_smith | and is being called with one arg, a list |
| 17:08 | TheLastProject | OtherRaven: Too late to fix that, heh... |
| 17:08 | justin_smith | so why use apply? |
| 17:08 | OtherRaven | TheLastProject: Tehehe |
| 17:09 | justin_smith | TheLastProject: are you passing in "upper" and expecting that to resolve to the var upper? |
| 17:09 | justin_smith | TheLastProject: that isn't quite how clojure works, I would suggest another lookup, the same way you look up the function |
| 17:10 | justin_smith | if you type upper into read-line, yes, it will be a string |
| 17:10 | TheLastProject | justin_smith: That's kinda what I was trying to do. Expecting, well, I don't expect too much when I don't know how a language works. Hmm, okay, let me think for a bit |
| 17:11 | justin_smith | TheLastProject: the early mistakes are the hardest, if you stick with it it all makes sense |
| 17:11 | justin_smith | clojure has different rules than most languages, but its rules are more self-consistent |
| 17:11 | justin_smith | and it has fewer of them |
| 17:12 | OtherRaven | TheLastProject: One thing that's good to do before sleeping is reading programming manuals. *hint hint* |
| 17:12 | bitemyapp | `cbp: looks good to me, thanks! |
| 17:13 | justin_smith | TheLastProject: so going back to the issue at hand - read-line gets the string "other", because that is what came in. A regex split on a string can only return a sequence of strings |
| 17:13 | justin_smith | if they were implicitly interpreted you would get very weird bugs |
| 17:13 | justin_smith | sorry, "upper" |
| 17:13 | justin_smith | so if you want "upper" to mean some collection you have defined when it comes in as text from the user, you have to set that lookup up |
| 17:13 | TheLastProject | justin_smith: I understand why it can't just be interpreted like that, it's just that I sometimes wished computers would know what I MEANT to type, instead of what I actually typed :P |
| 17:14 | TheLastProject | Ah, I got it working |
| 17:14 | TheLastProject | I'm sure there must be a better way, but this works for the time being |
| 17:14 | justin_smith | try this: (def collections {"upper" [...]}) then you can do a lookup on the other arg the same way you would with the first |
| 17:15 | justin_smith | coventry: yeah, I had to modify his code to play with it at all |
| 17:15 | TheLastProject | http://pastebin.com/6vGq7tpM |
| 17:15 | TheLastProject | Guess this is what it is for now |
| 17:15 | justin_smith | TheLastProject: an atom is used to store a value you want to update safely |
| 17:15 | justin_smith | TheLastProject: it seemed like upper was something you wanted to be able to update |
| 17:16 | TheLastProject | justin_smith: Yeah, I read the atom page, but my mind is unable to parse it at the moment |
| 17:16 | justin_smith | in this case, amalloy was right, you do want to use apply, so that x and y can be separate args in update-clothing |
| 17:17 | TheLastProject | Ah... |
| 17:17 | justin_smith | and then you could use a lookup map to look up the clothing collection, the same way you use a lookup map to look up the functions already |
| 17:17 | TheLastProject | I'm probably just too tired to figure out the right way to do this |
| 17:18 | coventry | TheLastProject: You'll probably have a smoother learning curve if you read existing code for a while, code particularly with commentary like in a programming manual. |
| 17:18 | justin_smith | (btw "amalloy was right" is a truism) |
| 17:18 | justin_smith | sure, learning takes a while |
| 17:18 | OtherRaven | yes, programming manuals are good |
| 17:19 | justin_smith | and no need to learn everything in one day, of course |
| 17:19 | TheLastProject | I always try to avoid those manuals because I find them boring, heh... |
| 17:20 | OtherRaven | Really? I find them entertaining. |
| 17:21 | OtherRaven | And if there's any language that's fun to read about, it's clojure. |
| 17:22 | justin_smith | oh, you want the DWIM instruction |
| 17:22 | justin_smith | I think that's a perl thing :) |
| 17:22 | amalloy | justin_smith: an emacs command, too: comment-dwim |
| 17:22 | justin_smith | nice |
| 17:22 | bitemyapp | TheLastProject: Clojure Programming is a pretty good book and will make certain you don't make unnecessary mistakes. |
| 17:23 | TheLastProject | DWIN? |
| 17:23 | amalloy | aw, no such thing as unnecessary mistakes. every mistake you make while learning is something you needed to learn not to do |
| 17:23 | amalloy | TheLastProject: do what i mean |
| 17:23 | TheLastProject | Heh... |
| 17:24 | bitemyapp | amalloy: I suppose, but I see a lot of people "grind" in here. |
| 17:24 | justin_smith | TheLastProject: it is a decent start |
| 17:25 | justin_smith | making it use an atom is as simple as wrapping the def like (def upper (atom ...)) and then using @ when reading and swap! or reset! to change the value |
| 17:27 | justin_smith | and check out "clojure programming" from oreilly and "the joy of clojure" |
| 17:27 | TheLastProject | Okay, I get it, I'm not getting out of reading :P |
| 17:28 | justin_smith | those books are not manuals |
| 17:28 | justin_smith | the manual is right in the repl |
| 17:28 | OtherRaven | the joy of clojure is nice, but too hard for a beginner |
| 17:28 | justin_smith | (doc fn) |
| 17:28 | clojurebot | "([& sigs]); params => positional-params* , or positional-params* & next-param positional-param => binding-form next-param => binding-form name => symbol Defines a function" |
| 17:28 | OtherRaven | or at least it was too hard for me when I tried to read it XD |
| 17:28 | justin_smith | (source fn) |
| 17:28 | justin_smith | you can learn a lot just by calling source on various things |
| 17:29 | TheLastProject | That's an actual clojure command? |
| 17:29 | justin_smith | ,(doc doc) |
| 17:29 | clojurebot | "([name]); Prints documentation for a var or special form given its name" |
| 17:29 | bitemyapp | ,(doc source) |
| 17:29 | clojurebot | "([n]); Prints the source code for the given symbol, if it can find it. This requires that the symbol resolve to a Var defined in a namespace for which the .clj is in the classpath. Example: (source filter)" |
| 17:29 | OtherRaven | function, yeah |
| 17:29 | bitemyapp | ,(source doc) |
| 17:29 | clojurebot | Source not found\n |
| 17:29 | bitemyapp | alright fine. |
| 17:29 | TheLastProject | Heh, \n |
| 17:29 | amalloy | ~def doc |
| 17:29 | amalloy | that's clojurebot-specific, though |
| 17:30 | justin_smith | in about fifteen minutes clojurebot will respond with "who's there?" |
| 17:31 | justin_smith | amalloy: heh |
| 17:32 | justin_smith | TheLastProject: DWIM - do what I mean |
| 17:33 | justin_smith | grind? |
| 17:37 | justin_smith | did I say 15 minutes? guess it is more like 7 |
| 17:38 | justin_smith | TheLastProject: yeah, doc |
| 17:38 | justin_smith | in your own repl, doc and source will work |
| 17:39 | justin_smith | they are often as much manual as you need |
| 17:39 | justin_smith | amalloy: doc and source are standard |
| 17:39 | justin_smith | ? |
| 17:39 | justin_smith | but ~def, yeah |
| 17:51 | justin_smith | an eloquent insight on learning to program: http://i.imgur.com/cBComRu.png |
| 18:04 | bitemyapp | justin_smith: perfect. |
| 18:32 | dotemacs | Hi, trying out liberator and I'm using the example the project has in it's documentation: (the 2nd example on this page: http://clojure-liberator.github.io/liberator/tutorial/decision-graph.html) but when I run it, the parameter is not being picked up, so I created this example which spits out the whole request: https://gist.github.com/dotemacs/7405304 |
| 18:33 | dotemacs | the problem i'm seeing is that query-string within the request has the 'word=tiger' yet, the params within the request is blank |
| 18:34 | dotemacs | what am I doing wrong here? |
| 18:35 | danielszmulewicz | Me says you need the wrap-params middleware |
| 18:35 | danielszmulewicz | :-) |
| 18:37 | danielszmulewicz | dotemacs: http://www.refheap.com/20698 |
| 18:43 | danielszmulewicz | change defroutes app to defroutes routes |
| 18:44 | danielszmulewicz | then define app like in the refheap |
| 19:12 | bitemyapp | arrdem: where did the pipelining stuff come from? |
| 19:21 | Morgawr | trivial question but.. in an agent if I provide error mode to be :continue it means that upon an error it will keep the same state as if the action on that agent had never happened, right? |
| 19:22 | Morgawr | so I can always be sure that sending stuff to that agent will never leave it in FAILED mode |
| 19:23 | bbloom | Morgawr: should be trivial to test that |
| 19:25 | Morgawr | bbloom: you're right, I was just wondering about irky situations like IO operations |
| 19:25 | Morgawr | but I can test it |
| 19:26 | Morgawr | mmm, IO is still executed but when the exception is encountered it's just ignored and the entity's state it kept intact |
| 19:26 | Morgawr | okay, that's a good explanation for me :P |
| 19:27 | bitemyapp | Morgawr: I just got done figuring a way to handle agent error modes and finalization |
| 19:28 | bitemyapp | Morgawr: in Revise, specifically. |
| 19:28 | Morgawr | bitemyapp: do tell :) |
| 19:30 | bitemyapp | Morgawr: well in this case I made a somewhat unusual connection management strategy for a database client library. |
| 19:30 | bitemyapp | Morgawr: agents were serializing access to the IO resource, in this case a socket and its associate in and output streams. |
| 19:31 | bitemyapp | Morgawr: sockets can die, lose connection, yadda yadda. |
| 19:31 | bitemyapp | in this case, it's better to take the Erlang approach, just let it break, and finalize the agent with error delivery to all "on deck" promises, basically telling the dependent callers to try again with a new agent. |
| 19:31 | Morgawr | interesting, yeah |
| 19:31 | bitemyapp | there's no point mutating the agent with a new socket in an attempt to recover, callers need to decide what's appropriate. |
| 19:32 | Morgawr | how do you notify the caller of the agent? |
| 19:32 | bitemyapp | in fact, attempting to do so could just cause cascading failure in situations like when a connection is refused or shutdown because the database is overloaded. |
| 19:32 | bitemyapp | Morgawr: promises. |
| 19:32 | bitemyapp | all query results happen through promises. There's an independent (as in, doesn't happen in the agent) reader thread for the socket too. |
| 19:33 | Morgawr | ah yeah |
| 19:33 | Morgawr | makes sense |
| 19:33 | bitemyapp | Morgawr: for more, see: https://github.com/bitemyapp/revise/ |
| 19:34 | Morgawr | bitemyapp: I'll give it a look, thanks |
| 19:48 | bitemyapp | ddellacosta: welcome. |
| 19:49 | ddellacosta | morning |
| 19:52 | bitemyapp | ddellacosta: dunno if you saw, but I ended up coming up with a satisfactory solution to the error handling thing last night. |
| 19:54 | Morgawr | mm... I'm trying to use a "PrintWriter" from java (java.io.PrintWriter) but I can't quite get it to work with clojure |
| 19:54 | Morgawr | I try to do java.io/PrintWriter |
| 19:54 | Morgawr | but it says it can't find the class |
| 19:54 | Morgawr | isn't that how it should work? or do I have to import it too? |
| 19:54 | arrdem | bitemyapp: in what sense? |
| 19:55 | arrdem | bitemyapp: as in where did I learn that stuff, or as in what's the reason for it? |
| 19:55 | arrdem | bitemyapp: or just why am I writing about it? |
| 19:56 | amalloy | Morgawr: the / is for separating classname from method |
| 19:56 | amalloy | the classname is just java.io.PrintWriter |
| 19:56 | Morgawr | amalloy: ah.. I see |
| 19:56 | Morgawr | thanks |
| 19:58 | ddellacosta | bitemyapp: sorry, I did see it but got busy at the very end of things and didn't respond. :-( |
| 19:59 | ddellacosta | bitemyapp: although re-reading it now, I wanted to know why you said it "demanded a monad" to paraphrase. I didn't quite get it. |
| 20:06 | bitemyapp | arrdem: I'm just curious about it in general. |
| 20:06 | bitemyapp | arrdem: also, up for a game? |
| 20:07 | muhoo | monads can be demanding. climbing up on your bed and pawingnat you because they want to be fed |
| 20:07 | arrdem | bitemyapp: I think I can get a box... lemme check |
| 20:07 | brehaut | muhoo: lolwat! |
| 20:08 | muhoo | brehaut: ddellacosta wanted to know why something demanded a monad |
| 20:08 | muhoo | damn monads, always making demands |
| 20:09 | ddellacosta | damn you, monads |
| 20:09 | ddellacosta | there ya go |
| 20:09 | arrdem | I shoulda used monads in that pipeline post... |
| 20:09 | arrdem | the vm state is really a monadic value... |
| 20:09 | muhoo | monads always sounds to me like a body part you wouldn't want injured |
| 20:10 | brehaut | if injured badly enough, you may be left with only a monoid |
| 20:10 | bitemyapp | arrdem: it is indeed. |
| 20:13 | bitemyapp | ddellacosta: I've acquired some materials on category theory, trying to form a more thorough understanding of how it all fits together. |
| 20:14 | brehaut | i was reading a basic category theory book yesterday. first three examples of categories were straight forward: set (got it), partially ordered set (got it), monoid (interesting, got it) and then Bam! a category for any abstract algebra (definately havent got it) |
| 20:14 | brehaut | welcome to page 4 |
| 20:15 | bitemyapp | saw an ordering (order by this, then that, then this) abstraction defined in terms of a Monoid. really tickled me. |
| 20:15 | brehaut | hah yeah |
| 20:15 | bitemyapp | brehaut: what book was this? the one I'm reading isn't bad so far. |
| 20:16 | brehaut | Basic Category Theory for Computer Scientists |
| 20:16 | bitemyapp | I'm reading "A Gentle Introduction to Category Theory - The Calculational Approach" |
| 20:16 | bitemyapp | they're a bit vague about morphisms (I'm on page 8), but things are going okay so far. |
| 20:16 | brehaut | i think morphisms are just anything that changes a category structures shape |
| 20:17 | brehaut | each morphism is quite distinct |
| 20:17 | brehaut | i found homomorphisms (on monoids anyway) to be relatively grokable and interesting |
| 20:18 | brehaut | basically (mappend (H a) (H b)) === (H (mappend a b)) (where H is a homomorphism) |
| 20:18 | brehaut | s/H is a/H is any/ |
| 20:18 | mtp | that smells like the distributive law |
| 20:19 | brehaut | mtp it might be, but its specifically defined in the context of categories |
| 20:19 | arrdem | bitemyapp: the gaming box is currently being used for stupid shit. I'll let you know if I manage to get an exclusive lock. |
| 20:20 | brehaut | or monoids at least |
| 20:20 | bitemyapp | arrdem: mutex lol |
| 20:20 | brehaut | so there are many Monoids around numbers, and around strings or lists |
| 20:20 | bitemyapp | anything concatenative or progressively accumulated, yeah. |
| 20:20 | sritchie | brehaut: fun time to come in :) |
| 20:20 | brehaut | yeah |
| 20:20 | brehaut | sritchie: haha :) im reaching the end of what ive learnt |
| 20:21 | bitemyapp | interesting from a distributed systems perspective is to what degree you can formalize "independence" between operations performed. |
| 20:21 | bitemyapp | once you abstract and "de-power" away from full turing completeness for the semantics of your operations, the closer you get to something robust, understandable, and scalable. |
| 20:21 | sritchie | I was playing around with simple-check and the monoid laws the other day - |
| 20:21 | sritchie | https://gist.github.com/sritchie/7406280 |
| 20:21 | brehaut | yes exactly. and you can trivially rewrite any expression between monoidal structures via homomorphism to order as your resources allow |
| 20:21 | bitemyapp | imagine if every query was a black-box program that mutates the state of the database? |
| 20:22 | bitemyapp | instead of something declarative. |
| 20:22 | bitemyapp | brehaut: one moment of minor confusion I've had is that this book defined fold as being commutative. |
| 20:22 | bitemyapp | that was a bit ?_? |
| 20:23 | bitemyapp | I guess that's true in some cases (reduce +), but it still threw me for a loop briefly. |
| 20:23 | brehaut | bitemyapp: i thing that if you define 'fold' as distinct from 'left fold' and 'right fold' then it may be true? |
| 20:24 | brehaut | (isnt that what the reducers library does?) |
| 20:24 | bitemyapp | brehaut: I think so, yes. |
| 20:24 | amalloy | brehaut: yes |
| 20:25 | brehaut | sritchie: *brain asplode* ;) |
| 20:25 | sritchie | I want to encourage more monoids in Cascalog, |
| 20:26 | sritchie | so users can write more general aggregations, then just extend the traits to their particular datastructures |
| 20:26 | brehaut | huh thats interesting |
| 20:26 | sritchie | making it trivial to move on from counts, to cardinality estimations, to moving averages, etc |
| 20:27 | sritchie | Summingbird was all about encouraging users to use values with Monoids defined; |
| 20:27 | sritchie | https://github.com/twitter/summingbird |
| 20:28 | sritchie | as you were saying, we can get pretty far with systems optimizations knowing algebraic properties alone |
| 20:28 | sritchie | associative - boom, we can add realtime and batch computed values together into one seamless, realtime looking value |
| 20:28 | sritchie | commutative gets you more optimizations, like mapside aggregation; and so on. |
| 20:30 | coventry | Is there a version of clojure.walk which preserves all metadata? E.g. |
| 20:30 | coventry | ,(require '[clojure.walk :as wk]) |
| 20:30 | clojurebot | nil |
| 20:30 | coventry | ,(->> [(with-meta (list 1) {:foo true})] (wk/postwalk identity) first meta) |
| 20:30 | clojurebot | nil |
| 20:31 | brehaut | sritchie: thats cool stuff |
| 20:32 | brehaut | sritchie: im fascinated by the sorts of program transformation you can undertake when you level the really 'weak' algebraic structures (in contrast to say monad or arrow) |
| 20:32 | sritchie | yeah man, some of the most fun stuff to work on has been the graph optimization layer - |
| 20:33 | sritchie | writing little optimizers that take the summingbird graph, tweak it, then pass it on to storm or scalding to get compiled down to a physical plan |
| 20:34 | amalloy | coventry: no |
| 20:34 | brehaut | sritchie: im curious if the programming world would have more excited about categories and algebras if they had heard about this stuff first, rather than mind benders like monads |
| 20:35 | sritchie | yeah, especially when you get into the whole "monad is a monoid in the category of endofunctors" set of one-liners |
| 20:35 | coventry | amalloy: Thanks. Any suggestions for a traversal framework which does? I am planning to try zippers next. |
| 20:35 | brehaut | sritchie: that one at least was intended as a joke i believe |
| 20:35 | sritchie | :) |
| 20:35 | brehaut | sritchie: but yes, it makes people blanch |
| 20:35 | sritchie | but no one knows it! |
| 20:36 | brehaut | sritchie: have you ever asked fsbot in emacs about monads and comonads? |
| 20:36 | brehaut | in #emacs |
| 20:36 | sritchie | brehaut: have you played with any of the approximate data structures that have been coming out, like hyperloglog, count min sketch, etc? |
| 20:36 | sritchie | haha, no |
| 20:36 | brehaut | its worth joining just to try it |
| 20:36 | amalloy | nothing i know of, coventry |
| 20:37 | sritchie | "[Too many DB matches] Accent on helpful side of your nature. Drain the moat." |
| 20:37 | sritchie | :) |
| 20:38 | brehaut | haha |
| 20:38 | bitemyapp | brehaut: I just tried it, hahahahaha |
| 20:38 | bitemyapp | sritchie: yeah brehaut is right, it's worth trying. |
| 20:39 | brehaut | the co- stuff in category theory made a lot more sense to me once i realised categories were all about topologies |
| 20:39 | brehaut | (and that it just swaps the domain and codomain for the various arrows) |
| 20:39 | sritchie | brehaut: I remember asking you about monads a couple of years ago, and you taking the time to explain as well as you could via PM |
| 20:40 | sritchie | my mind was blown - now, after the Scala years, it's all clear |
| 20:40 | sritchie | but I haven't gone after comonads |
| 20:40 | brehaut | sritchie: haha cool. i vaguely recall now too. |
| 20:40 | brehaut | ive looked at comonads from a distance |
| 20:40 | sritchie | I ended up writing a thrift parser using a parser monad: |
| 20:40 | sritchie | http://github.com/sritchie/thrift-clj |
| 20:40 | bitemyapp | I can name a few examples of comonads but I don't really have a grip on it beyond a recitation of what it does at the category theoretic level. |
| 20:41 | sritchie | brehaut: a la your tutorial |
| 20:41 | brehaut | oh right. cool :) |
| 20:41 | amalloy | i've briefly come to an understanding of comonads, a few times |
| 20:41 | bitemyapp | amalloy: slippery. |
| 20:41 | brehaut | sritchie: im curious how the performance is |
| 20:42 | brehaut | and im glad to see you switched to instaparse |
| 20:42 | sritchie | brehaut: I wanted to compare it to an instaparse version via mark engelberk |
| 20:42 | sritchie | engelberg |
| 20:42 | sritchie | now that I'm gone from Twitter working clj full time, it's more likely to happen |
| 20:42 | justin_smith | hey brehaut, you said you do web stuff with ring, right? |
| 20:42 | brehaut | justin_smith: yeah |
| 20:42 | bitemyapp | I think most Clojurians, when and if they do web stuff, do it in Ring :) |
| 20:43 | brehaut | one hopes ;) |
| 20:43 | sritchie | I've been playing with liberator, btw |
| 20:43 | sritchie | I was asking this earlier - |
| 20:43 | sritchie | though I'll stop if no one here knows liberator |
| 20:43 | sritchie | it feels like there should be a built-in way to define base behaviors, then define resources off of merged maps of decision points |
| 20:43 | sritchie | easy to write, of course |
| 20:44 | brehaut | so far ive only looked at liberator from the outside. havent had a need to use it in anger |
| 20:44 | justin_smith | brehaut: you may be interested, I made a ring middleware to store, retrieve, and replay ring requests https://github.com/noisesmith/groundhog |
| 20:45 | brehaut | haha i love the name |
| 20:45 | bitemyapp | justin_smith: that's pretty cool |
| 20:46 | bitemyapp | now I want to watch that movie. |
| 20:47 | bitemyapp | justin_smith: for the body, which might be binary, why not base64? |
| 20:48 | bitemyapp | oh you did a byte-array anyway, n/m |
| 20:51 | john2x | what's the best way to replace all `x` values in a 2D grid with `y`? |
| 20:52 | arrdem | john2x: how's the grid represented? |
| 20:55 | justin_smith | heh |
| 20:56 | justin_smith | I left it kind of wide open in terms of storage if you want anything other than a global atom in the lib itself |
| 20:56 | justin_smith | eventually I may add some helper functions for db storage file storage etc. |
| 20:56 | justin_smith | but it's just edn so I figure people can roll their own |
| 20:56 | john2x | [[nil nil nil 0 nil][nil nil 1 nil nil]] and I want to replace the numbers with strings in an array ["foo" "bar"], using their index. [[nil nil nil "foo" nil][nil nil "bar" nil nil]] |
| 20:57 | justin_smith | bitemyapp: it is base64 |
| 20:57 | justin_smith | with the reverse automatically being done on the way out |
| 20:58 | justin_smith | yeah, I wanted to keep strict method compatibility, so any middleware would get exactly the kind of data they expect |
| 20:58 | justin_smith | within reason |
| 20:58 | egosum | hmmm, anyone have any tips for nicely formatting JSON? I'd like to emulate something like this: http://jsonformatter.curiousconcept.com/ |
| 20:58 | justin_smith | don't pass it a long arbitrary stream in the request body and expect anything sane :) |
| 20:59 | john2x | arrdem: [[nil nil nil 0 nil][nil nil 1 nil nil]] and I want to replace the numbers with strings in an array ["foo" "bar"], using their index. [[nil nil nil "foo" nil][nil nil "bar" nil nil]] |
| 20:59 | justin_smith | ie. if we read faster than you write, we assume you are done and we store what we have so far |
| 21:01 | arrdem | john2x: you'll need two mapv applications, identity, contains? and a map. |
| 21:01 | esehara | nick esehara |
| 21:02 | arrdem | bitemyapp: not getting a box tonight |
| 21:02 | justin_smith | john2x: I bet there is a concise way to do that with for |
| 21:02 | arrdem | justin_smith: not really unless you want to ... oh. yeah there probably is. |
| 21:03 | john2x | justin_smith: yeah, for and an atom to hold the mutating grid was my first thought. but was wondering if there was a more functional way. |
| 21:04 | arrdem | john2x: no you can definitely do this using only for. for is lazy. the for application you're thinking of won't work. |
| 21:05 | john2x | OT: hmm is it just my client or is arrdem's mentions of me sending doubles? |
| 21:07 | arrdem | john2x: ... wat |
| 21:07 | esehara | hi, Clojuren. I'm developer in Japan. I wish to translate official document to Japanese. Do you have good approach about it? |
| 21:08 | bitemyapp | arrdem: nuts. |
| 21:08 | brehaut | john2x: no its just you |
| 21:08 | brehaut | john2x: no its just you |
| 21:08 | john2x | heh. I guess it's just me. Right, I keep on forgetting that for is lazy. How would I approach this for? |
| 21:08 | bitemyapp | john2x: no idea what you're talking about |
| 21:08 | bitemyapp | john2x: no idea what you're talking about |
| 21:08 | john2x | ah, brehaut's mentions are in doubles too |
| 21:08 | john2x | everyone's! |
| 21:08 | brehaut | we are messing with you |
| 21:09 | llasram | Plan succeeded? |
| 21:09 | john2x | lol |
| 21:10 | brehaut | apparently im too honest to gaslight |
| 21:10 | egosum | (bump one more time) Does anyone know of any libraries for printing JSON nicely for e.g. APIs (formatted like something like this: http://cl.ly/image/2Z0o3y1N3d3u) |
| 21:10 | justin_smith | john2x: ((fn [grid] (for [x grid] (for [y x] (get {0 "foo" 1 "bar"} y y)))) [[nil nil nil 0 nil][nil nil 1 nil nil]]) |
| 21:10 | arrdem | john2x: ok I lied. you need two fors. |
| 21:10 | arrdem | john2x: but it is doable with only for. |
| 21:11 | justin_smith | ,((fn [grid] (for [x grid] (for [y x] (get {0 "foo" 1 "bar"} y y)))) [[nil nil nil 0 nil][nil nil 1 nil nil]]) |
| 21:11 | clojurebot | ((nil nil nil "foo" nil) (nil nil "bar" nil nil)) |
| 21:11 | amalloy | justin_smith: ##(doc replace) |
| 21:11 | lazybot | ⇒ "([smap coll]); Given a map of replacement pairs and a vector/collection, returns a vector/seq with any elements = a key in smap replaced with the corresponding val in smap" |
| 21:11 | egosum | Oh my, Chesire does that… https://github.com/dakrone/cheshire |
| 21:11 | arrdem | john2x: https://www.refheap.com/20707 |
| 21:12 | arrdem | amalloy: damnit |
| 21:12 | justin_smith | john2x: clojurebot takes forever to respond to me, but if you try that in a repl you will see it gives the output you specify |
| 21:12 | justin_smith | also ignores input not in the mapping |
| 21:12 | justin_smith | (passes identity) |
| 21:13 | justin_smith | arrdem: ? |
| 21:13 | amalloy | weird that replace is special-cased for vectors to return another vector |
| 21:13 | amalloy | rather than just returning a seq in all cases |
| 21:13 | justin_smith | john2x: yeah, the version I posted aboce is pure functional |
| 21:13 | justin_smith | for is not imperative in clojure |
| 21:14 | justin_smith | the coincidence that it has the same name as the default imperative mutate a collection construction is too bad |
| 21:15 | amalloy | justin_smith: do you have a really bad internet connection, or do you just have a weird plan for reading the scrollback? you keep falling silent for several minutes, and then writing a half dozen messages at once as if you were in some past conversation |
| 21:15 | amalloy | clojurebot's slow responses to you support the first theory |
| 21:15 | justin_smith | arrdem: I actually did this already using nested for, clojurebot will eventually spew out the correct results. He has a lag on my input for whatever reason. |
| 21:16 | justin_smith | just you |
| 21:17 | amalloy | yeah, definitely bad internet connection. clojurebot responded to you in one second |
| 21:18 | arrdem | justin_smith: oh. right. I saw that you posted a solution but I didn't really read it. |
| 21:18 | arrdem | justin_smith: you have bested me, brb falling on my perentheses |
| 21:19 | justin_smith | john2x: you would approach it in exactly the way I pasted above, it is working code that produces the output you asked for |
| 21:21 | amalloy | &(map (partial replace ["foo" "bar"]) [[nil nil nil 0 nil][nil nil 1 nil nil]]) |
| 21:21 | lazybot | ⇒ ([nil nil nil "foo" nil] [nil nil "bar" nil nil]) |
| 21:21 | amalloy | is rather nicer imo |
| 21:23 | justin_smith | amalloy: good point |
| 21:24 | justin_smith | handy |
| 21:26 | justin_smith | amalloy: ok, something is messed up with my internet clearly |
| 21:26 | justin_smith | that would explain the lag of clojurebot's responses |
| 21:26 | justin_smith | and people acting like they haven't heard me |
| 21:28 | justin_smith | thanks for helping me figure that out, it was kind of gaslighting me :) |
| 21:29 | justin_smith | arrdem: lol |
| 21:30 | john2x | awesome. thanks guys. I'm really finding it hard to think differently about `for`. |
| 21:31 | arrdem | john2x: the critical thing is that for isn't the classical for.. it's a list comprehension. we have (doseq) for what you typically think of as (for). honestly (foreach) would be a better name than for... |
| 21:34 | john2x | yeah, but even then, there's no way I could've thought about using map, partial, replace to achieve what I wanted. |
| 21:34 | hyPiRion | It's about training |
| 21:34 | amalloy | practice, practice, practice |
| 21:35 | amalloy | arrdem: i don't think foreach is a meaningfully different name |
| 21:36 | john2x | yeah. it's gonna take a *lot* of effort to make the paradigm shift. I've just recently gotten the hang of working with immutable data (and I love it). |
| 21:37 | arrdem | amalloy: once you bring the :when/:while option in it really isn't. I was just thinking about the specific case of a sequential "update" as here. |
| 21:41 | justin_smith | john2x: I wish they had picked a different name for 'for'; like maybe 'cartesian' |
| 21:43 | justin_smith | ,(for [x (range 4) y (range 4)] [x y]) ; cartesian |
| 21:43 | clojurebot | ([0 0] [0 1] [0 2] [0 3] [1 0] ...) |
| 21:44 | hyPiRion | 'for' is confusing because it's used differently in imperative languages |
| 21:46 | hyPiRion | There is one function though... `contains?` anyone? |
| 21:46 | seangrove | contains? |
| 21:46 | clojurebot | contains? checks whether an indexed collection (set, map, vector) contains an object as a key. to search a sequence for a particular object, use the `some` function, which accepts a predicate. if you want to only match a certain object, you can use a set as the predicate: for example, (some #{5} (range)) finds the first occurrence of 5 |
| 21:47 | seangrove | Love me some `contains?`, find myself reaching for it much sooner these days |
| 21:48 | hyPiRion | oh, humm... |
| 21:49 | hyPiRion | ,(let [a (doto (java.util.ArrayList.) (.add 10)) b (java.util.ArrayList.) coll #{a b}] (.add b 10) coll) |
| 21:49 | clojurebot | #{[10] [10]} |
| 21:50 | hyPiRion | obviously, don't do that. |
| 21:54 | bitemyapp | I use contains? whenever the query I am performing makes sense in an associative context |
| 21:54 | bitemyapp | arrdem: wait, so can you play LoL without borrowing a box, or was that for general purposes? |
| 21:55 | hyPiRion | yeah, I only use it on maps and sets |
| 21:56 | bitemyapp | hyPiRion: associative by key, and associative by value. I find the associative-by-index semantics of vectors a bit discomfiting when you start using stuff better suited to maps/sets |
| 21:58 | arrdem | bitemyapp: that was for general purposes. I haven't tried running sc2 or lol under Arch but it's worth a shot. |
| 22:02 | justin_smith | hyPiRion: woah, that is, like, spooky |
| 22:03 | bitemyapp | hyPiRion: why does the ArrayList look like a vector? |
| 22:03 | bitemyapp | hrm, so you can mutate references inside of immutable collections. Yeah, that's not a fantastic idea. |
| 22:04 | hyPiRion | justin_smith: not really. It's just that maps cannot update themselves (and shouldn't) when "values" update |
| 22:05 | hyPiRion | bitemyapp: not sensible for mutable collections either! |
| 22:42 | dobry-den | Seesaw is amazing |
| 22:46 | bitemyapp | ucb: http://www.youtube.com/watch?v=9vL9zCFpv-0 |
| 22:46 | arrdem | bitemyapp: sc2 under Arch was the failz |
| 22:46 | bitemyapp | ucb: sorry if I've already sent it to you, I thought of it and figured it was good for you. |
| 22:46 | bitemyapp | arrdem: attempted Wine I take it? |
| 22:46 | arrdem | bitemyapp: with unfortunate results.. part of ~/ got rm'd. |
| 22:47 | bitemyapp | :| |
| 22:47 | bitemyapp | dafuq kinda amateur hour... |
| 22:48 | coventry | How does that happen? |
| 22:50 | arrdem | nothing lost tho... ~/config bit it, but that's under version control. restoring now... everything else that got hit is symlinks so no problem. |
| 22:53 | coventry | Oh, good. It'd really suck if an application misbehaved that way. |
| 22:55 | TimMc | arrdem: `rm -r ~/ .wine`? |
| 22:55 | arrdem | TimMc: something like that, yeah. |
| 22:55 | TimMc | Ouch. |
| 22:56 | arrdem | TimMc: C-c saved the day, and git more crontab'd backups are making everything right. |
| 22:56 | arrdem | I'm just suffering from amature hour as bitemyapp was kind enough to remind me. |
| 22:56 | hyPiRion | I'll give you guys a great trick I've learnt |
| 22:56 | coventry | I've made the same mistake, although a long time ago. I generally do an ls first, then back in the rm -rf. |
| 22:56 | TimMc | May I recommend installing trash-put and aliasing rm to it? |
| 22:57 | bitemyapp | arrdem: amateur* |
| 22:57 | arrdem | bitemyapp: go away |
| 22:57 | TimMc | It was a dick thing to say. |
| 22:57 | bitemyapp | TimMc: I was actually assigning the blame to Wine. |
| 22:57 | hyPiRion | I have a file named '--foo' in my home directory, so if I ever do 'rm -rf *', I'll get an error message about a bad flag instead |
| 22:57 | bitemyapp | TimMc: it was his confession that made me aware the rm'ing of his ~ was his fault. |
| 22:58 | coventry | "rm bitemyapp" "rm: cannot remove `bitemyapp': Permission denied" |
| 22:58 | TimMc | bitemyapp: Ah, that's an important distinction. |
| 22:58 | TimMc | You're thinking of e.g. the bumblebee incident? |
| 22:59 | TimMc | hyPiRion: Nice. |
| 23:00 | technomancy | justin_smith: looks like you forgot to license groundhog? |
| 23:00 | technomancy | also :source-paths in project.clj is redundant |
| 23:00 | TimMc | ls: unrecognized option '--false-flag' |
| 23:01 | TimMc | bitemyapp: https://github.com/MrMEEE/bumblebee-Old-and-abbandoned/issues/123 |
| 23:03 | TimMc | Or rather https://github.com/MrMEEE/bumblebee-Old-and-abbandoned/commit/a047be85247755cdbe0acce6#diff-1 |
| 23:05 | bitemyapp | TimMc: sigh, troll threads on github. |
| 23:05 | justin_smith | wasn't it npm that started removing people's directories for one of its updates? |
| 23:05 | bitemyapp | justin_smith: to be fair, disabling the computers of node.js users is a service to mankind. |
| 23:06 | bitemyapp | <SHOTS_FIRED.JPG> |
| 23:06 | justin_smith | https://github.com/isaacs/npm/issues/2293 maybe is what I was thinking of? |
| 23:07 | bitemyapp | "and I enjoy reinstalling things :)" <--- stockholm syndrome |
| 23:08 | justin_smith | ,'HELLO |
| 23:08 | clojurebot | HELLO |
| 23:08 | justin_smith | ok, it was emacs' fault |
| 23:37 | muhoo | sure, blame emacs |
| 23:37 | justin_smith | well, the restart fixed the issue, so.. |
| 23:42 | muhoo | TimMc: holy shit. github->4chan |