Saturday, January 29, 2011

Best Buy and Monitor Suspension

Last weekend, I placed an order for something that's been a long time coming.

The same order will also contain a new wireless keyboard I'll be trying out. It does something a bit odd with the arrow keys, but other than that, seemed to work pretty well when I tried it in store.

It probably won't replace my keyboard of choice, rather it'll function as a nice spare for working on off machines. It does have a much smaller receiver than the wireless keyboard I currently use in the livingroom, so it might find a permanent home there.

Now I'm used to ordering shit from newegg.ca, maybe I'm a bit spoiled, but this order was far from error free. So, for the benefit of people who want to get decent pricing on monitor arms, here's a guide to ordering things from BestBuy.ca with BestBuy gift cards.

Step 1 - Shop

Find the stuff you want and get it into your shopping cart (this works the same as on every online store ever).

Step 2 - Checkout

Click "Checkout". If you've never ordered something from BestBuy before, you'll need to register by telling them your email and setting your BestBuy password.

Step 3 - Address

Fill in your shipping and billing addresses (if you don't have them memorized, make sure to write at least the phone numbers down; you'll need to enter them a few times and the last phone field doesn't autocomplete for whatever reason).

Step 4 - Payment Information

Get your gift card out and enter the 16 digit number from the back (be sure to omit the spaces, the BestBuy devs don't believe in .replace()) then scratch off the security strip and enter the confirmation code. If you like, you can check the balance on your card before continuing just to make sure. Once you've entered your gift card info, click "Apply". This should cause your order to error, saying "ERROR: This order cannot be processed at this time. Please try again later.".

Step 5 - Address

You can't refresh, or hit back (well, you can, but you'll be prompted to refresh, at which point it will leave you with a blank form anyway) so just click on your cart and start the order again. Re-enter your addresses (if you just clicked "Add to Address Book" in step 3, note that your billing address is now entered in the shipping address fields, and your billing address fields are empty).

Step 6 - Payment Information

Re-enter your gift card numbers and click "Apply". This time your order should go through. After a short loading screen, you will be told that your "credit card" was declined (if you took more than 5 minutes to correct your address for whatever reason, you will instead be told that it has been 30 minutes since any activity and your connection has been terminated; either way proceed to the next step).

Step 7 - Address

Re-enter your addresses (make sure to correct the shipping address again).

Step 8 - Payment Information

Enter your gift card information. This time the order should go through (for realsies). You should get an on-screen receipt, which will contain your order number. Print this page (or at least save it as a PDF); you should get a copy emailed to you, I just printed mine to be safe.

Step 9 - The Aftermath

You will be sent a confirmation email, saying that your order has been accepted and will be shipped in two days. If your order contains items from multiple suppliers, you will get one confirmation per supplier. You will also get an email saying that your payment method was declined by the second supplier. This will happen even if you have enough money on the gift card. The second supplier will try to charge your gift card again the following day (you won't get a confirmation email if this succeeds but it should, with no action on your part, as long as you have enough balance remaining). Wait two days to see if the order ships before calling CS; they're very nice people who will tell you no more than I just did, and they can't seem to help much in this situation.

And that's it. You've just ordered something from BestBuy. It'll be there in about four days via Canada Post expedited delivery. By "there", I mean "at the billing address".

Monitor Arm (Atdec VF-AT-D)

First impressions are pretty good, except that it didn't come with a key screw that would let me use the clamp option, so I'll need to drill a hole in my desk this weekend before I mount stuff. It's fully articulated and there's a pretty wide range of configurations possible. The main win for me is that I'll reclaim a lot of my desk space because the arm is tall enough to keep my monitors above the working area (and I can move them out of the way in any case). It'll also even out my monitor position options; currently I have a Dell 23" widescreen (which came with a fully rotatable/tilting stand), and an NEC 22" widescreen (which can basically just tilt about an inch).

[one installation later...]

It definitely evened out my setup; both of my monitors are now comfortably hovering 12" above my desk (right at eye level, which is the ergonomically correct placement as I understand). The reason that I know it's exactly 12" is that I had to use a 12" steel ruler to brace the monitor arm in order to keep it from sliding down the shaft. I'm going to Home Depot later this week to get a locking clamp, but in the meantime, the ruler is performing admirably. The arm says it's built for a pair of 24"/17.5lbs monitors. It's not. What they probably meant is that the joints won't warp or sever under that weight. It's doubtful anyone at the company actually tried to put this thing together with two 24" monitors, because they would have quickly realized that the main clamp doesn't grip strongly enough to keep the setup in place (or, they tried it with two featherweight monitors and didn't bother to note the weight difference in their media).

TL;DR;

Pros:

  • Ergnomic positioning (and actually, really good cable management) for monitors.
  • High degree of articulation (not as much as the stand on a Dell Profesional series monitor, but still respectable).
  • Small footprint.

Cons:

  • Be prepared to drill a hole in your desk for it (even if the correct screws had come with the unit for clamping, there are some positional limitations if you go that route)
  • Be prepared to brace the main shaft if you have larger than 20" monitors (currently using a steel ruler in the wire cleft at the back, really should be using a locking ring clamp to support the on that came with it).
  • If you have especially small/lightweight monitors and can pick this up cheap, go nuts, otherwise I honestly can't recommend it. I'm keeping mine because it's better than stacking books under my NEC to get it to eye level, and because of the small footprint, but I probably would have gone with another model had I known how flimsy the locking assembly is on this one.

Monday, January 24, 2011

XMonad Up Close

I'm taking another, closer look at XMonad this week. StumpWM is awesome for straight-up coding, and its extensive use of the Emacs model means that there was very little learning curve for me to struggle against. A few things were starting to burn my ass though, and while I tolerate small blemishes there are two other forces at work here. First, I've tried hard to make sure that my tools enable rather than limit me (and one of the things that started to burn my ass was a specific GIMP-related bug that did limit me). And second, I've been looking for an excuse to pick up Haskell for more than casual play for a very long time now.

As a public service, here are the few issues that I ran into with StumpWM (I don't intend to dwell on this, just be aware of them. I can still recommend it heartily if you're an Emacs user that won't run up against these specific points):

  1. It crashes every time I try to open a file with GIMP
  2. It has some odd issues with Mplayer (naive fix here).
  3. It doesn't seem to like nautilus (which is mainly an issue when I'm trying to browse through a folder full of images; this is one of the few places in my day-to-day computing activities where a command line is less efficient than a GUI)

That's it. Now, to be fair, #3 is only relevant if you don't use StumpWM as a window manager on top of a desktop environment, #2 has a workaround that I've been using successfully, and #1 only really bites you if you're multiclassing Illustrator/Programmer, which is not unheard of, but keep in mind that YMMV here.

It's actually sort of amazing that I got by for such a long time without noticing #1. After noticing it, I got into the habit of spending most of my time in StumpWM, and switching into Gnome/Bluetile for GIMP work. And that worked out just fine when most one or the other type of work was a vast majority of my time. Sadly (fortunately?) a couple weeks ago, my schedule started rebalancing into about 50/50 graphics and coding (I'm doing some concept work, which involves a web app, but no code yet so my tablet and degree are finally being put through their paces). It was surprising how horribly annoying the start-up wait time for Gnome/Bluetile can be. I've written about it already, and my conclusion on Bluetile was, basically, that it was overtly complex but a workable beginners' TWM. Certainly not something I'd use as my first choice, in any case. Add to that the fact that I had been spoiled by StumpWMs' nearly instantaneous start-up, and those WM switches were starting to look ugly. It actually changed the way I thought; I'd get all my coding done first, then do my image work all at once, just to minimize the impact of that switch.

This was clearly not an optimal situation.

By chance, I stumbled onto a reddit post bemoaning that Gnome lag. Long story short, the poster used XFCE4, XDM and Ubuntu server edition to put together a minimal, but snappy desktop environment. It looked interesting, and passed the Compaq Test[1] so I took the weekend to replace Gnome with XFCE4 on each of my machines (I kept them all Debian Squeeze, rather than downloading Ubuntu server 10.10, and I used xmonad instead of XDM because I primarily wanted tiling rather than mousing). There's bound to be more updates about this as I nail down specific stuff, but it's working very well so far. I have tiling where I need it, and (because of XFCE4) I can use pointing devices when they're appropriate. My ~/.xmonad/xmonad.hs is pretty minimal at this point:

import XMonad
import XMonad.Config.Xfce

import XMonad.Actions.Submap
import XMonad.Util.EZConfig

import qualified Data.Map as M

main = xmonad $ xfceConfig { modMask = mod4Mask }
       `additionalKeys`
       [ ((mod4Mask, xK_p), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
       , ((mod4Mask, xK_Return), spawn "xterm")
       -- , ((control, xK_space), spawn "xdotool text 'testing testing'")
       -- , ((controlMask, xK_t), submap . M.fromList $
       --                         [ ((0, xK_p), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
       --                         , ((0, xK_Return), spawn "xterm")
       --                         , ((0, xK_t), spawn "xdotool key ctrl+t")
       --                         ])
       ]

The commented stuff has to do with tweaks I'm trying to make. Xmonad+XFCE4 hits all of the pain points I was having with StumpWM, but it introduces a couple of its own (less severe, from my perspective).

First, the mod-keys aren't on the home row; I have to contort my left pinky/ring finger/thumb (still haven't decided which feels least uncomfortable) in an odd way to hit the Win or Alt keys in a way that never happened when mod was effectively C-t. Granted that may have made it very slightly more awkward to add tabs in some browsers, but there are workarounds. Also luckily, XMonad.Actions.Submap exists, which means I can write up a key list that's less RSI-inducing (the only reason this is commented above is that I can't get xdotool working as advertised).

Second, there's that layer of user-facing complexity that comes from distinguishing between screens and workspaces. I've had time to reflect since my Bluetile writeup, and it seems like a lot of time in StumpWM gets spent screen-juggling (making sure that Emacs and Chrome stayed on my main monitor and mplayer/terminals/secondary apps on the other one). This is because Stump doesn't make that key distinction. When you cycle to the next window, it's the same window you've got open (regardless of which workspace you've currently got it on). That's easier to learn because you have to think about a single list of windows, but trickier to use because it's that much more likely to blow your layout by pulling a window you don't mean to. XMonad goes the other way; there are three explicit keystrokes to switch the "focused" monitor (and as far as I can tell, if you have more than 3, you're screwed), but the upside is that windows will stay where you put them, workspace-wise.

It also looks like I'll have to do some light Haskell learning to get full benefit, but if you've ever talked to me about computers IRL, you know that I don't consider that downside.


1 [back] - "The Compaq test" is something I put any desktop environment or WM through before switching; it involves building it on a $20 Compaq Presario R3000 (with a 1.4ghz processor, a whopping 256 MB of ram and a 5400rpm HDD) and using it for a weekend to see how it works. The reasoning is that if it's tolerable on the Compaq, then it'll fly on my desktop machine. It's something I reserve for background software, not things like Emacs or GIMP themselves. My rule of thumb is that anything I'm thinking of using that has to be on all the time should do better than "awful" on the Compaq test.

Thursday, January 13, 2011

Writing C in Lisp

I'm sick today, so I figured I'd write something so that I can at least retain the impression of productivity.

Lately, I've been working on a little pet project of mine which has to do with the codebase for Elite for Emacs (a port of Ian Bell and David Braben's original Elite game for my favorite editor). I liked the original, and while I didn't play it in 1984 (too busy being born that year), I did enjoy it quite a bit. The Emacs version is text driven, of course, but that's not all bad. I'll come back to why in a later post, but first I want to heap some scorn on this code. It's not that I hate it particularly. It works, for the most part, and it doesn't have to be fast (performance requirements are unsurprisingly low in a single-player game in Emacs). It's just that whenever I try to read any piece of elite-for-emacs, I come away with the impression that someone sat down and carefully thought of the worst possible way to do something, then implemented it. The result is an extremely verbose transliteration of a C program into Emacs Lisp. I'm going to dissect it here so that I can learn something, and so that hopefully, if there are hardcore C programmers out there learning Lisp, they won't pull things like this again.

This didn't start out as a re-write, just so you know.

I just wanted to fix a couple of bugs with weird control characters showing up in planet descriptions, change the behavior of a couple of commands for ease of use, and remove one or two components. It won't be a rewrite in the "throw it out and start over sense", but after peeking under the hood, it looks like I'll replace very close to all of the 4205 lines of code that make up this "port" of the original (either C or Assembly) codebase before I'm satisfied. There are some mistakes. Not in the sense that they don't produce working code, but in the sense that there are much simpler, easier and more accurate ways of doing the same thing in Elisp. Here are some before shots of common idioms I've found in no particular order:

(if foo
    (progn bar
           baz))

(defun foo (a)
  (let ()
    (progn
      (baz)
      (mumble))))

(if foo
    (progn (setq b bar))
    (progn (setq b baz)))

(defun foo ()
  (let ((a)
        (b)
        (c))
    (setq a bar)
    (setq b baz)
    (setq c mumble)
    (progn
      ...)))

(let ((a)
      (i 0)
      (b ()))
  (while (< i (length foo))
    (progn
      (setq a (car foo))
      (setq b (append b (list (mumble a))))
      (setq foo (cdr foo))
      (setq i (+1 i))))
  b))

If you want to see the complete, non-elided code, check out the project page. It's pretty much more of the same, with some odd byte/numeric indexing operations thrown in where they probably don't belong.

As far as I can tell, the snippets above should have been respectively

(when foo bar baz)

(defun foo (a)
  (baz)
  (mumble))

(setq b (if foo bar baz));; I'm leaving the setq in for demonstration purposes, but the actual task could have been done functionally

(defun foo ()
  (let ((a bar)
        (b baz)
        (c mumble))
      ...)) 

(mapcar #'mumble foo)

I'm not pointing this out to be mean. This code was written way back in 2003 (I'm using an earlier version that doesn't include combat/missions/GUI/windowed interface because my purposes demand simplicity, but the later code still makes use of the above idioms from what I saw). It's possible that some of this stuff was necessary at the time because of bugs in Emacs, or the peculiarities of Elisp. Not terribly likely, but possible. Anyway, here's why I don't like the above.

  • The when macro exists. (when foo bar baz) does bar and baz if foo is true (unless is a similar construct that does exactly what you'd expect given when). Keep in mind that in Elisp, (), '() and nil are "false" and anything else is "true" for boolean purposes.
  • The last value in a lisp block is returned implicitly. you can use this property to chain calls on objects instead of explicitly setfing an intermediate variable then copying. This applies to if too, which is why you can do (setq b (if foo bar baz)) instead of having to put setq in both branches of the conditional.
  • You don't need to declare variables in lisp. If you want to establish local bindings (let ((a 1) (b 2) ...) [body]) is the way to do it. You can also use let* if you want the temporary variables to refer to each other, for example (let* ((a 1) (b (+ 3 a))) b) would return 4. You do need to keep the two straight in your head, because (let ((a 1) (b (+ 3 a))) b) would return an error (specifically, it would complain that the variable a is unbound). This is because let doesn't guarantee that its bindings will be done in the order they are presented. let* does this, but it's considered good style to use let where you can. If you need to define temporary functions, use flet.
  • progn isn't necessary everywhere. Use it if you need to do multiple things in one branch of an if statement. Keep in mind that when, unless and cond have implicit progn for their blocks, so you don't need to type it out. New Lisp coders might think this is analogous to the missing curlies problem in C-like languages. I've been chewed out for doing things like
    if(foo) bar();
    else baz();
    in javascript code. The argument is always that if someone later adds mumble to the else block, but forgets to add curly braces, they'll get a fairly hard-to-find bug.
    if(foo) bar();
    else mumble();
         baz();
    
    In case you didn't catch it, that makes baz() unconditional, which is presumably not what you want. The correct way of doing it, I'm told, is
    if(foo){
        bar();
    } else {
        baz();
    }
    or
    if(foo)
    {
        bar();
    } 
    else 
    {
        baz();
    }
    depending on who's talking. In an imperative language with optional curlies/parentheses/brackets, this matters, so I'm not arguing that you should all stop using curly braces except where explicitly required. However, the fact that Lisp is fully parenthesized almost makes this a non-issue, a functional style mitigates it further, and in any case, adding progn all over the place isn't the right way to address it.
  • It's very common in Lisp to want to iterate over a sequence, do something to each member of that sequence, and return the resulting list. The name given to this oddly specific idea is "mapping". The specific function you use is called different things (map, mapcar or similar) depending on which language you're in, and there are one or two subtleties (for instance, Elisp's mapcar only takes a unary function and a single sequence, Scheme's map can only take lists and not any sequence, etc.), but odds are that if you search the docs of a given functional language for "map", you'll find a function that does the above. When you're dealing with a sequence of things, it's a mistake to use while with an explicit, manual counter. I'd use mapcar or similar, and fall back to recursion (with tail calls where applicable) for more general purposes.

There are more things I could gripe about, and my sinister purpose in toying with this code is unrevealed, and I feel like I've only started off a much larger conversation about what it actually means to learn to program in a given language, but I think I need to go lie down now. This got enough stuff off my chest that I can continue to wade through the code for a while without bitching internally.