I finally got a nice laptop

Well, I finally broke down and did it.  For Amazon's Prime Day this past week, I ordered myself a new laptop.  And this time, it's actually a nice one.

Acer Spin 5 and Lenovo IdeaPad U310 side by side

The old laptop

My previous laptop was a Lenovo IdeaPad U310 that I bought almost 8 years ago.  I know because I looked up the blog entry.  It's actually still in pretty good shape, which is why I haven't replaced it.  It already had decent horsepower (a Core i5, upgraded with 8GB of RAM) and I upgraded it with an SSD about three years ago, so it still performs reasonably well for my purposes.  In fact, my son will be inheriting it so that he has a non-school laptop he can use. 

On the other hand, it is 8 years old.  And while it still works well, it's starting to show its age - the screen hinge is starting to wear out and the WiFi adapter is unreliable.  On top of that, I've been spoiled by having relatively good laptops at work, so I kinda just wanted something nicer.

While the IdeaPad is a solid laptop, it's not particularly fancy.  It's an ultrabook, but a low-end one - I only paid $650 for it in 2013.  It's got decent build quality and a touch screen, but that's about it for amenities.  No keyboard lighting or stylus.  The screen is workable, but not great - the main problem being that it has a very narrow viewing angle.  It's not particularly light (about 3.7 pounds according to Lenovo's specs) and it tends to run a little hot when under any kind of load.

The new laptop

I looked at several of the laptops on Prime Day special, but after going back and forth several times, I eventually decided not to compromise and just spend a little extra money on something nice.  I eventually settled on an Acer Spin 5, which was on sale for $880.  Normally, I'd be too cheap to spend that much, but I figured that as long as I was getting a new laptop, I might as well make it a noticeable upgrade.

Acer Spin 5 with stylus out

And in terms of features, this is a significant upgrade.  Of course, being a new system, the Spin 5 has more horse-power, with 16GB of RAM, a Core i7 processor, and a 500GB NVMe drive.  However, it's the other features that are what make the big difference for me.  For starters, the Spin 5 is a convertible, i.e. it has a 360 degree hinge and can fold into a tablet mode.  (My wife's Dell Inspiron 7000 is also a convertible and I've always thought it looked like a cool capability.)  To complement that, it comes with a nice powered stylus that stows in the body of the laptop.  The keyboard is backlit (which I didn't used to think I cared about, but really does make a difference when you want to work in low light) and the touchpad has an integrated fingerprint reader that you can use to log in with Windows Hello (which is a dumb name, but whatever).

The screen is very nice.  It's a 13.5" IPS display with a 3:2 aspect ratio and very thin borders at the edge.  This actually feels much bigger than the old IdeaPad, even though it has a 13.3" display, but at a 16:9 aspect ratio.  I actually think that this alone may have been worth the price.  The increase in vertical screen space is immediately noticeable and makes the laptop much more pleasant to use, whereas the smaller display on the IdeaPad often felt a little cramped. 

I'm also enjoying the ultra-portable aspect of the Spin.  It's very thin and only weighs about 2.6 pounds.  That makes it very easy to carry around and keeps it from feeling unwieldy in tablet mode.

I've only had it a few days, but so far I don't have many complaints.  I do find the fact that home/end the function key for page-up/down kind of annoying.  Especially when print-screen and pause/break (which I use much less frequently) both have dedicated keys.  I guess I'll get used to it, but it's still dumb.  I also didn't like that, for the top row, F1-F12 are the function keys and the media features are the default.  Luckily, that's easily fixed with a BIOS setting.  One other minor weirdness is that the 3:2 screen means that the system is a slightly weird shape for a laptop.  I mean, it does fit into the bag I use for the IdeaPad, but just barely - it's got plenty of room on the sides, but it's almost too tall to close the bag.

All in all, I'm pretty happy with the Spin 5 so far.  I'm still getting the software set up, so we'll see how it performs with some regular use, but I don't anticipate any problems.  And I'm already using the tablet mode for casual web browsing and finding it just as handy as I'd hoped.  I don't think I'm ever going to be able to go back to a cheap laptop again.

Stupid Vim tricks - putting the time on the status line

The other week I realized that I was often moving my mouse for no reason while coding.  Well, not no reason - I wanted to see what time it was.  You see, I have Windows set to hide the taskbar because I use 13" laptops and want to maximize my vertical screen space.  But that means that I have to hit the Windows key or move the mouse down to bring up the clock.

I don't have this problem when browsing the web, because Vivaldi has a setting to put a clock in the lower-right corner of the window.  So I thought to myself, "Self, do you think Vim can do that same thing?"

Well, it turns out it can!  In fact, there's even an article on it.  Of course, that's for putting it in the plain-old status line.  I'm using the airline plugin, so I had to tweak it a little.  Still, I was able to get the passable looking image above by putting the following in my .vimrc:

" Put the current date and time in the status line and keep it updated
let g:airline_section_z='%p%% %#__accent_bold#%{g:airline_symbols.linenr}%l%#__restore__#%#__accent_bold#/%L%{g:airline_symbols.maxlinenr}%#__restore__#:%v %{strftime("%b %d %I:%M:%S%p")}'
let status_update_timer = timer_start(1000, 'UpdateStatusBar',{'repeat':-1})
function! UpdateStatusBar(timer)
  execute 'let &ro = &ro'
endfunction

The timer update the time every second (obviously you can do longer if you want).  That's because, otherwise, the time won't update until you do something in the editor window.  This way it stays current, even if I'm not actively editing.

PHP documentation and sockets

PHP's documentation gets way too much credit.  I often hear people rave about how great it is.  Many of them are newbies, but I hear the same thing from experienced developers who've been writing PHP code for years.

Well, they're wrong.  PHP's documentation sucks.  And if you disagree, you're just plain wrong.

Actually, let me add some nuance to that.  It's not that the documentation sucks per se, it's that it sucks as documentation

You see, a lot of PHP's documentation is written with an eye to beginners.  It has lots of examples and it actually does a very good job of showing you what's available and giving you a general idea of how to use it.  So in terms of a tutorial on how to use the language, the documentation is actually quite good.

The problem is that, sometimes, you don't need a tutorial.  You need actual documentation.  By that, I mean that sometimes you care less about the generalities and more about the particulars.  For instance, you might want to know exactly what a function returns in specific circumstances, or exactly what the behavior is when you pass a particular argument.  Software is about details, and these details matter.  However, PHP frequently elides these details in favor of a more tutorial-like format.  And while that might pass muster for a rookie developer, it's decidedly not OK from the perspective of a seasoned professional.

Case in point: the socket_read() function.  I had to deal with this function the other day.  The documentation page is rather short and I was less than pleased with what I found on it. 

By way of context, I was trying to talk to the OpenVPN management console, which runs on a UNIX domain socket.  We had a small class (lifted from another project) that basically provided a nice facade over the socket communication functions.  I'd noticed that, for some reason, the socket communication was slow.  And I mean really slow.  Like, a couple of seconds per call slow.  Remember, this is not a network call - this is to a domain socket on the same box.  It might not be the fastest way to do IPC, but it should still be reasonably quick.

So I did some experimentation.  Nothing fancy - just injecting some microtime() and var_dump() calls to get a general idea of how long things were taking.  Turns out that's all I needed.  It quickly became obvious that each call to the method to read from the was taking about 1 second, which is completely absurd.

For context, the code in that method was doing something like this (simplified for illustration):

$timeoutTime = time() + 30;
$message = '';
while (time() < $timeoutTime) {
    $character = socket_read($this->socket, 1);
    if ($character === '' || $character === false) {
        break;  // We're done reading
    }
    $message .= $character;
}

Looks reasonable, right?  After all, the documentation says that socket_read() will return the number of characters requested (in this case one), or false on error, or the empty string if there's no more data.  So this seems like it should work just fine. 

Well...not so much.

The problem is with the last read.  It turns out that the documentation is wrong - socket_read() doesn't return the empty string when there's no more data.  In fact, I couldn't get it to return an empty string ever.  What actually happens is that it goes along happily until it exhausts the available data, and then it waits for more data.  So the last call just hangs until it reaches a timeout that's set on the connection (in our case, it was configured to 1 second) and then returns false.

So because we were relying on that "empty string on empty buffer" behavior to detect the end of input, calling that method always resulted in a one-second hang.  This was fairly easily fixed by just reading the data in much larger chunks and checking how much was actually returned to determine if we needed another read call.  But that's not the point.  The point is that we relied on what was in the documentation, and it was just totally wrong!

And it's not like this is the first time I've been bitten by the PHP docs.  Historically, PHP has been very bad about documenting edge cases.  For example, what happens if a particular parameter is null?  What's the exact behavior if the parameters do not match the expected preconditions?  Or what about that "flags" parameter that a bunch of functions take?  Sometimes the available flags are well documented, but sometimes it's just an opaque one-line description that doesn't really tell you what the flag actually does.  It's a crap shoot.

To be fair, the PHP documentation is not the worst I've ever seen.  Not even close.  And it really is very good about providing helpful examples.  It's just that it errs on the side of being light on details, and software is details.