Decklin Foster: man ascii

Thank you for replying instead of simply downvoting. There are assumptions in the structure of the original text that I don't think most of the people discussing it (here, or over there) appreciate. To wit: the narrative of technology belongs to men. Women are objects. The dumbest, easiest way to make that stick out (I did it over breakfast, ffs) is to swap the genders. It sounds "wrong". The emotional-abuse stuff is more creepy. The physical descriptions are more awkward because men aren't leered at in the same way. It's not about evening things out, it's about subverting what the text doesn't say outright by making it obvious. I am, I'm sure, as aware as you that "deconstruction" is not (anymore) a word you say to sound smart, rather a word you avoid saying to avoid sounding like you're trying to be pseudo-smart, but sometimes you just gotta call a spade a spade. You're free to disagree with that (I assume you still will), but I think "not even wrong" is a little over the top.
Fetched 1B in 0s (42B/s)Well. That settles that question! :-)
Twitter : IRC :: Blogs : Usenet(This applies equally to other "micro-blogging" services, but I am about to explain why I believe that's not the right metaphor. You may also substitute mailing lists for Usenet.) With the older media, you have a place -- a newsgroup, or a channel, that people went to, with a distinct culture, and that (mostly) weren't "owned" by anyone, but rather by the community. With the new ones, we are all sole proprietors of our own streams, and we "tune in" to the subset of people we find interesting, rather than topics we invest in. So, instead of bumping into the same person in a couple different groups, or never reading their words at all, you might find that your feeds overlap a bit more than they do with most people. This is how I find people to "follow" and blogs to read, in fact -- as my network expands, more people become loosely joined to it, and as I notice ones worth reading I add them. Is one model better than the other? Probably not. I could make an analogy to music. In theory, I'd rather read what my favorite critics have to say about a wide variety of new releases -- some of which I'd never know about otherwise -- than keep up with the discussion of bands and genres I really like, even if most people writing about them are terrible (remember, 90% of everything is crap). But I also lose that sense of community of being a "fan" of something; I no longer have a deep connection to what's going on in the fandom or the scene, which I also would never know about otherwise (some of it is just too obscure for my favorite writers to cover). In practice, it seems the new approach has been more popular, but maybe that's because more people are on the net now, and both kinds of communication are/were shaped by the technology available at the time (destinations make much more sense with limited/centralized computing resources, and aggregation makes much more sense with powerful clients and a wider, less specialized user base). Anyway. This post is not actually about social media theory or whatever you want to call it; it's about some software I have packaged. Because of all the above, I have always wished that I could use Twitter from something more like my IRC client. Like, say, my IRC client. One could abuse the concepts of IRC to make an "on the fly" channel of whoever happens to be in my feed. (I once read a blog comment somewhere complaining that Twitter could easily be implemented on any existing IRC server using one +m channel for everyone and some client-side direction of messages from all such channels to a single window. +1 for cleverness, but they did sort of miss the point of why normal people sign up for web sites rather than installing and configuring clients for obscure chat protocols.) So, I had grand plans to write a Twitter client which was an IRC server, with some clever mapping of IRC concepts and commands to their equivalents over there. I never got around to it, as I barely have enough time for anything I do now. But last month I noticed that someone else had implemented such a thing: tircd. I had seen Twitter/IRC services before, but like the official Twiter XMPP service, they were all implemented as bots, which I detest for this sort of application. Bitlbee, for example, translates various IM protocols to IRC, but only halfway -- for anything else you have to use a bot as a sort of poor man's command line. If I want a command line for Twitter, I already have several; IRC can do better. And tircd really does! It's great. You're not required to edit the config file, and there's no extra layer on top of IRC for things like logging in or adding people. I've finally packaged the latest release, which is waiting in NEW currently. I got a request for sneak preview packages, so if you want to install some unapproved .debs check this repository. I may still pick my own project back up, as Twitter itself, being a centralized service, feels like a stopgap solution on the way to to a more generalized 140-character equivalent of the, er, blogosphere as envisioned by open-source projects like Identi.ca. In the future, perhaps, when we use a certain micro-blogging "service" we might be randomly connected to one of any number of servers run by different individuals but all mirroring messages back and forth to each other. Which, now that I think of it, sounds vaguely like some obscure, obsolete chat and news-posting protocols I know.
git filter-branch \ --tag-name-filter cat \ --index-filter 'git update-index --remove .hgtags' --commit-filter \ 'if [ $# = 3 ] && git diff-tree --quiet $1 $3; then skip_commit "$@" else git commit-tree "$@" fi' \ HEADThe tag name filter is always necessary if you want tags to be updated to point to the corresponding commits on the new, rewritten branch. I consider this a UI failure -- when a branch is rewritten, the ref is modified, and the old one moved to refs/original. Tags, on the other hand, stay where they are, without any indication on the new branch that this is where you might want to move that old tag and sign it again or whatever. IMHO they ought to be handled the same as branches. The index filter is simply an efficient way of removing the unwanted file from all commits. This and the tag filter are both covered in the manual page. Writing a commit filter is a little more obscure. After .hgtags is removed from the index, we may end up at one of those useless "Added tag foo" commits and have no changes to record in the commit. By default, of course, filter-branch still records these -- the commit message might be useful, or something. But I want to suppress them. The commit filter is called with a tree -- you're at the point between write-tree and commit-tree (I recommend Git from the bottom up if you're confused here.) It gets that tree ($1), and then "-p PARENT" for each parent, just like commit-tree. So, if this is a normal commit with one parent, there will be 3 arguments. (If there's only one argument, there is no parent, i.e., the first commit, and if there are more, then it's a merge.) This is the only case we want to mess with. If there are no changes between our tree and the parent's tree, then it's one of those no-op commits, and we can skip it (skip_commit, a shell function defined by filter-tree, uses some deep magic to hand us the original parent again next time). I think diffing the index and the parent would work as well, but this seemed clearer. It still feels like a hack, so I'd love to hear from anyone who can suggest improvements. Since this is a special case, maybe it's better off being implemented in hg-to-git.py itself. There's always more than one way to do it. Update: Teemu Likonen points out that the next version of Git (1.6.2, not yet in unstable) will have a --prune-empty option which makes this particular problem totally trivial. I am starting to get the feeling that the Git developers are all reading our minds... :-)
class Server < Struct.new(:host, :port, :password) def to_s port == 6600 ? host : "# host :# port " end endThat particular one's from Njiiri (which may clue you in that it took me several months to get around to writing this post). For educational purposes, it's nice: you can point out how we give a name to a class (I've also tried to explain assignment as "naming" rather than "storing"), that there's an unnamed class (classes are also objects!), and overriding a method (which uses the dynamically defined stuff), without any boilerplate to get in the way. So it's tempting to use this in a lesson. Unfortunately, Ruby is a little bit weird here, and you run into those distracting "practical" issues about the particular language you're working in. Your classes can't subclass Class, and thus you can't say Foo.new like you can Struct.new. Struct is actually implemented in C (or Java, or whatever's native). So you have to do some handwaving. This is because the "metaclasses" we have in Ruby are implemented, so to speak, as singleton classes of objects (including class objects). I do not mean this as a criticism of Matz at all, but they seem like more of a serendipitous thing than an intentional design -- "hey, if we implement classes in this particular way, we get this sort of metaclassing automatically [1]." (The clearest explanation of why this is that I've found is in the first chapter of Advanced Rails by Brad Ediger.) I wanted to be able to just subclass Class, rather than have all that fun power that we normally get to abuse in Ruby, only because I think this is the clearest way to explain the abstraction. Even I still have trouble describing singletons in plain English. Struct is intuitively a class of classes, and factors out similar/boring stuff -- a good practice. It could be an example of how to refactor some simple classes, if only we could follow it. I decided to give up on the idea for my tutorial, but it kept me up at night. Ruby metaclasses clearly can do anything that the sort of Struct-like metaclasses I have in my mind -- "parametric" classes, if you will -- can do, and I can dynamically define just about anything; why not make it happen? We can instantiate Class itself, but the tool we have to shape that class is singleton methods. This can certainly be abstracted away. So, I whipped something up. Before getting to it, though, let's visit a new construct in Ruby 1.9 and 1.8.7: Object#tap. Apart from the obvious debugging use described in its documentation, it makes it quite easy to factor out this pattern:
def gimme_a_thing thing = Thing.new thing.do_stuff_to_it thing endInto something closer to the style of functional or declarative programming:
def gimme_a_thing Thing.new.tap thing thing.do_stuff_to_it end end(Well, "do stuff" is obviously still procedural, but A for effort.) Which one is "better" could probably be the subject of much debate, but: I really prefer the second one; even though it has exactly the same effect, it looks like "what" rather than "how" (something else I try to beat into impressionable young heads. :-)), which is I think easier to write tests for. I'm going to use it here, because it's shorter. Now, Object#tap passes the object as an argument to the block; in our case, we are going to define a metaclass, so we want to work with self, instead. So we define a new version, class_tap -- by analogy with class_def, I suppose --- which class_evals the block rather than simply evaluating it:
class Class def class_tap(&blk) tap _self _self.class_eval &blk end endAnd to do the following trickery, we make use of MetAid, written by why the lucky stiff -- it's very small, so we could always just incorporate the bits we want into the code here, but this short file provides a common vocabulary for talking about metaclass stuff which is quite valuable. Now, we can write a method to create our new classes on the fly. Here's what I came up with:
require 'metaid' class << Class def meta(_super=Object, &blk) new.class_tap do # 1 meta_def :new do *args # 2 Class.new(_super).class_tap do # 3 class_exec *args, &blk # 4 end end end end end(Note the "class << Class", opening the singleton, rather than "class Class", opening Class itself. Also, the distinction between new and Class.new -- they are the same method, but from inside the meta_def we're no longer in the Class class.) The lines of meta itself mean:
Nary an assignment in sight! The (embarrassing) caveat is that methods in the block that defines the new class instance cannot use def to create methods as normal. A def is evaluated in a totally fresh scope (I don't think I get the opinion behind this decision, to be honest), so we need to use class_def instead. This is, I must admit, rather hideous. Perhaps someday the language will change. But, first things first: now we can implement Struct in pure Ruby.
- The value of this thing is a generated class, which we will describe thusly:
- Its singleton class has a new method, which gives you a value that is:
- Another generated class, which is defined by:
- the original block, which now gets run with new's arguments.
class MyStruct < Class.meta \ do *args attr_accessor *args class_def :initialize do *instance_args args.zip(instance_args).each do attr, val instance_variable_set :"@# attr ", val end end end endThe real Struct class does a few other nice things for you, but this is the heart of it; I can go back to that Njiiri example and just swap in MyStruct for Struct (Providing an instant performance gain of -200%... :-)). Here's a "shapes" example from my abandoned OO lesson:
class Polygon < Struct.new(:sides) def perimeter @sides.inject(&:+) end end class RegularPolygonClass < Class.meta(Polygon) \ do n_sides class_def :initialize do side_length @sides = [side_length] * n_sides end class_def :area do @sides.size * @sides.first**2 / Math.tan(Math::PI / @sides.size) / 4 end end end class Square < RegularPolygonClass.new(4); end class Pentagon < RegularPolygonClass.new(5); end(Note that area has no free variables and thus could actually be defined with def. It would just look funny.) You only have to change one number here to make new polygon classes, rather than accumulating parameter lint or explicitly subclassing and redefining something implicit just for the derived class [2]. In a way it the exact analogue of the imperative vs. tap style described above. There is quite a bit of aesthetics involved; one way is not "right". Apart from the syntactical wart, I like being able to do things this way. Ruby is, as they say, optimized for programmer happiness and the principle of least surprise. Still, I'm sure it is quite slow, and I don't particularly need it for any real-world application right now. For an intro to OO it's way too much complexity to have lurking unexplained beneath the surface and still requires getting bogged down in the language you happen to be using. But hey! It's neat.
[1] | I basically try to do this whenever possible, anyway. Design decisions are like sex: they're better when they're free. |
[2] | Of course, if Ruby class variables were not leaked across the entire inheritance hierarchy, something as trivial as this one-variable example would be... trivial. At least, few people would actually miss metaprogramming features. Blub blub blub. |
[1] | Incidentally: Linode has been great. Highly recommended. |
[2] | Also: yeah, I know my footnotes are broken. Working on it. |
Hello, please use https://joker.com/goto/support/ for all support inquiries. Regards, your Joker.com teamThis is complete and utter bullshit [2]. If a company is going to mail me, I expect to be able to mail them. I have a mail client; it manages communication the way I want it to, and runs my preferred editor to compose messages (which is, in fact, how I am writing this very blog post). I am sick of dicking around on JavaScript-requiring web forms that all work differently and typing in postage-stamp-sized little textareas. Anything I do type in them is lost, because unlike my email client, web forms don't save sent messages unless the authors feel like letting you have a Cc:. When a company forces me to do this, I take it as a sign of disrespect. I tolerate a web browser [3] for reading pages; "applications" are universally painful [4]. One of the things I used to like about Joker was the PGP mail interface. AFAIK, they have not killed it (I didn't bother to check), but with automatic "we don't want to listen to you unless you inconvenience yourself" bounces like this, what's the point? I surmise that there is, or was, some smart person there who understood how (and more to the point, why) to hook a pseudo-mailbox up to a software system, and that they have been overridden by someone in management who realized it's much easier to have dime-a-dozen webmonkeys hook a form up to the same system since 90% of users just don't care (and even people who do notice and dislike this, but are not as inflamed as I, have come to expect it because everyone else refuses mail, so, y'know, pick yr battles son, etc.). Not the sort of culture I put faith my in. The irony of it all was that I was registering this domain to run a service I had decided to create specifically because of another site refusing mail and directing me to an even lamer web form. That would take incoming mail and, you know... process it with software. Suffice it to say, I am no longer going to be their customer. In deciding who to use instead, I figured I'd do a survey of where the domains in that "Subscription" column [5] on Planet Debian were registered, but... WHOIS is basically useless. Every server just returns free text, formatted differently by (apparently) every implementation under the sun. Cheaply parsing something out from .com/.org/.net is possible, but InterNIC kindly blacklists your IP after making more than a few requests in a few minutes. I guess I'm just gonna go with Gandi (but other suggestions would be welcome). In somewhat unrelated developments, I went to nic.at to update my nameservers for Where the Bus At? last week. There was no authentication or anything on the request form, so I just filled it in and sent it off. I got some automatic mail saying I need to print out a PDF, sign it, and fax it internationally. Annoying, and hardly as secure as mailing them with PGP, but whatever. But then, also yesterday, I got another mail saying the update was complete (lo and behold, it was). I am now somewhat concerned about the security of my domain: it seems like anyone can come by and put something in the form and if I'm not around to notice the courtesy mail and ask that they stop the request, it'll eventually go through, no questions asked. I have not yet written them to figure out what the deal is, though. Kinda burned out. I guess I didn't really have high hopes for dealing directly with a ccTLD registrar (this was the first time I've done it... I can't believe I blew 60 on a cutesy domain name) rather than a reseller who competes in a market, but then, I go and google "domain registrar" and look at all the AdWords dollars spent trying to compete with GoDaddy [6] and just kind of want to put my head in my hands. On DJB's DNS pages there's this bit about setting up a domain. It doesn't say "How to (buy register whatever) a domain name". It says, "How to receive a delegation from .com". Which is of course, how it works. And what I want to buy. I don't want "parking" or even gratis nameservers. Just a delegation from .com. Please. No AdWords came up when I googled for that phrase to copy the link. Sometimes I guess markets just sink to the bottom. Anyway. I feel like there's a free-software angle here. My continuing irrational hatred of using other people's forms, web-based mailing-list substitutes, nameservers, etc. stems not so much from their suckage but from the fact that there is no longer any software there, in front of me, for the four freedoms to possibly apply to. Being able to run your mail reader for any purpose doesn't win you much if no one uses mail. I don't really know what to do about this.
[1] | It was only twelve dollars! If I go to hipster market or some other place with brand-new POS systems they don't even make me sign a paper slip for less than $20 or so. But this is the sort of thing dreamed up by people who think good security is setting a cookie to denote that I've answered "what was your first dog's name" or whatever. IF YOU WANT TO BREAK INTO MY BANK ACCOUNTS: I've set the answer to every single one of these questions to "security theater". Easy to remember. |
[2] | Not to mention a glaringly RESTless URI. |
[3] | Also, as a rule I normally try not to have anything up on my desktop that I can't close and re-open at any time, thanks to screen, MPD, emacsclient, actually using my browser's bookmark function and resisting the lure of tab-bar.js, etc. Web browsers are supposed to suck at preserving state; HTTP is stateless. See also here (and for further reading on that in the Haskell world, check out Yi. I'm hoping to switch to it someday). |
[4] | Debbugs, for example, may not have the slickest interface, but if I want to do flags, labels, archiving, threading, or whatever, I can; I'm not at the mercy of some front-end web developer. But we've all heard this litany before. |
[5] | Thanks, Hpricot! |
[6] | Even if GoDaddy's customer service were completely hacker-friendly, I would not use them, because their president Hates America. |
Despite all of those wins, sup currently has many drawbacks, and I don't recommend it for everyone. (And I mean everyone who thinks that the above are good ideas and are interested in using it; plenty of people, I'm sure, already think everything about this is idiotic, not new, or inferior to their preferred MUA. That's fine! You can ignore it all.) Here's what's still problematic:
- Sup has no folders, a la Gmail. After watching many friends and even fellow hackers switch to Gmail, I have to admit: this literal hierarchical organization thing doesn't scale. I was planning to totally redo my mail folder system Any Day Now for about six months prior to starting on this. It was never going to happen.
- Sup uses a Ferret full-text index to make this approach plausible. Search is super fast and beats (for me) both any kind of "organization" I could have disciplined myself into and the fine-grained control of something like mutt's search. It's sort of like git: until you do it, you don't realize how much more productive you can be when previously-expensive operations become instantaneous.
- Sup works with threads, not messages; this is another thing Gmail got right. I used to waste brain cells thinking about which messages in a thread were worthwhile enough to save or not. Given the absurdly cheap price of disk relative to what we can type out in plain text since, like, a decade ago, this is crazy. In the index, I only have to look at whether a thread has new chatter or not, not its size, shape, or where the new messages are relative to it. All that's in the thread-view buffer where I actually read content.
- Sup is written in Ruby. Back in the dawn of time, I used Gnus, and while I wasn't very good at elisp, the hackability afforded by being written in a high-level language was very nice compared to programs mostly implemented in C (even if they had a tacked-on scripting language). Plus, I love Ruby right now.
So, if you're interested enough that you want to deal with these warts for now, apt-get install sup-mail (as soon as it hits the archive) and join us! Hopefully being in Debian will increase the userbase and get things fixed faster. If you're unsure, stay tuned for the next-generation version later. (There are screenshots and a few introductory docs over at Rubyforge that illustrate and explain all this in more depth, which I recommend checking out if you're still saying, "...huh." Me, I'm a sucker for any piece of software with a manifesto.)
- At version 0.6, sup is very much not-yet-1.0. While it handles insanely large amounts of email without breaking a sweat, I still keep an additional backup of everything. (If Ferret crashes, the original copies of mail will be untouched, but it never hurts to be paranoid.)
- The flow of data from your physical mail store to the sup index is currently one-way only. Actually removing deleted/spam messages is a big hack (if it works at all), and labels/flags/etc live entirely in Ferret-land. If you want to manipulate an actual mailbox, mutt is still the tool for the job (and then, you need to re-sync sup). This is probably the deal-breaker for most of us. I jumped in anyway because I feel like it can be solved (or more likely, made irrelevant) later.
- William (upstream) is currently re-designing the whole thing from scratch, replacing the index library with Sphinx, and decoupling the index from the console frontend. As a result, the previous item is pretty much a non-priority (and bugs in general are not going to get the same amount of love as usual). I am hoping that we end up dumping mail into the index directly, then writing more frontends to write to Maildir backup, serve as webmail/whatever, but this is a long way off. On the plus side, thanks to Thrift, they will not be limited to Ruby.
- Ruby's ncurses library still doesn't handle Unicode correctly. It can be patched (still doesn't work totally right), but I'm trying to find a more permanent solution for Debian.
"Obama had the additional skill of criticizing George W. Bush."Basically: yes. And that's what passes for politics over here, folks. Sorry about your economies and all that.
Next.