All Posts (92)

Wow. Just yesterday I opened one of my favorite books, Revolution in the Valley, containing war stories about the creation of the Macintosh computer. I was reading all of the introductory matter carefully (the scans of an engineer's notebook) when I noticed that one of the pages contained signatures of several original members of the Mac team. Note in the accompanying photo the signatures of Woz, Bill Atkinson, Daniel Kottke, Caroline Rose, Andy Hertzfeld, Bud Tribble, and Susan Kare (with the ⌘ symbol).

This is really cool. It's funny to think that this is inside a book I bought for $12.99 from a used bookstore in Victoria BC several years ago.

Read more…

Literate Programming is a style of programming invented by Donald Knuth in which, instead of the usual style of embedding comments in code, you instead embed code in comments. The result is a long narrative of prose (describing how you are writing the code) with code snippets interspersed throughout.

This never caught on. Perhaps because programmers don't like writing documentation? However, I have noticed something interesting. Sophisticated programmers today write lengthy commit messages. It is an example set by the Linux kernel committers - check out their wonderfully descriptive commit messages. (I first learned about the high quality of Linux kernel commit messages from 5 Useful Tips For A Better Commit Message.)

It strikes me now - I wonder if commit messages are the new Literate Programming. It is said that programmers don't like writing documentation, but they seem to willingly put a lot of effort into writing long, descriptive commit messages. Here's an example of a meaty commit message from the Linux kernel:

commit d940878632e63d7f0c2af5e4ebfcf5135c48dcfb
Author: Jan Kara <>
Date:   Wed Jul 11 23:16:25 2012 +0200

    jbd: Fix assertion failure in commit code due to lacking transaction credits

    ext3 users of data=journal mode with blocksize < pagesize were occasionally
    hitting assertion failure in journal_commit_transaction() checking whether the
    transaction has at least as many credits reserved as buffers attached.  The
    core of the problem is that when a file gets truncated, buffers that still need
    checkpointing or that are attached to the committing transaction are left with
    buffer_mapped set. When this happens to buffers beyond i_size attached to a
    page stradding i_size, subsequent write extending the file will see these
    buffers and as they are mapped (but underlying blocks were freed) things go
    awry from here.

    The assertion failure just coincidentally (and in this case luckily as we would
    start corrupting filesystem) triggers due to journal_head not being properly
    cleaned up as well.

    Under some rare circumstances this bug could even hit data=ordered mode users.
    There the assertion won't trigger and we would end up corrupting the

    We fix the problem by unmapping buffers if possible (in lots of cases we just
    need a buffer attached to a transaction as a place holder but it must not be
    written out anyway). And in one case, we just have to bite the bullet and wait
    for transaction commit to finish.

    Reviewed-by: Josef Bacik <>
    Signed-off-by: Jan Kara <>
    (cherry picked from commit 09e05d4805e6c524c1af74e524e5d0528bb3fef3)
    Signed-off-by: Willy Tarreau <>

Reminiscent of Literate Programming, no?

Read more…

Lisphp at Ning - Sample Code

Here is the Lisp that I have written as an experiment with Lisphp. It is probably not great Lisp code—I don't have prior experience with Lisp—so any suggestions for improvement are welcome. I encourage others to make their Lisphp code publicly available so that we can all learn. Another Lisphp example I have found is balrog.

This code is for rendering an activity feed (like a Facebook Wall). It takes a bunch of activity-item objects, processes them, then feeds them into a Mustache template. The processing step sets up variables for the Mustache template to use.

For new Lisp programmers, I recommend the use of an editor plugin that automatically indents your Lisp code. See my first Lisphp blog post for related recommendations.


;;; Top-level module for rendering an activity feed for the network.
;;; @param string title  the title for the activity section
;;; @param array feed-events  the feed-event objects, parsed from JSON
;;; @param integer excerpt-length  the length at which to excerpt activity items
;;; @param string like-type  the naming scheme for likes: like, promote, or favorite
;;; @param string network-name  the name of the network
;;; @param string network-url  the URL of the network
;;; @param string network-icon-url  48x48 network icon
;;; @return string  HTML for the network feed

(import 'lib/components/activity/lib/process-feed-events.lisp')
(import 'lib/components/activity/lib/add-type-specific-properties.lisp')

;;; Filters, caches, and processes the given network feed events.
;;; @param array feed-events  the feed-event objects
;;; @param array options  options: excerpt-length
;;; @return array  the feed-event objects after processing
(define (process-feed-events feed-events options)
        ;; Remove rolled-up feed events for now, until we implement handling
        ;; for them.
        (setf! feed-events (remove-rollup-feed-events feed-events))
        (setf! feed-events (add-basic-properties feed-events options))
        (setf! feed-events (add-type-specific-properties feed-events options)))

(setf! processed-feed-events
       (process-feed-events feed-events
                            (hash 'excerpt-length'   excerpt-length
                                  'like-type'        like-type
                                  'network-name'     network-name
                                  'network-url'      network-url
                                  'network-icon-url' network-icon-url)))

;; Send the feed-events into the Mustache template.
((-> B renderMustache)
   'title' title
   'feed-events' processed-feed-events))


;;; Functions for processing activity events.

(use floor)
(use xg_elapsed_time)

;;; Removes rolled-up feed events. This is a temporary measure until we
;;; implement handling of rollups.
(define (remove-rollup-feed-events feed-events)
        (filter (lambda (feed-event)
                  (not (array-get feed-event 'rollUpType')))

;;; Adds basic properties, such as humanReadableDate, to each of the feed events
(define (add-basic-properties feed-events options)
        (map (lambda (feed-event)
               (let* ([content-id (array-get feed-event 'event' 'properties' 'contentId')])
                 (arr feed-event
                      (hash (event-type feed-event) true
                            'humanReadableDate'     (human-readable-date feed-event)
                            'content'               (to-content-properties content-id (at options 'excerpt-length'))))))

;;; Returns the event type suffixed with "Type", e.g., createBlogPostLikeType.
(define (event-type feed-event)
        (. (array-get feed-event 'event' 'eventType') 'Type'))

;;; Returns a friendly date for the event.
(define (human-readable-date feed-event)
          ;; Prefix timestamp with @ so that strtotime will understand it
          (. '@' (floor (/ (array-get feed-event 'event' 'createdDate') 1000)))
          nil nil false))

Read more…

Lisphp at Ning: Custom functions

In my previous post, I mentioned that we are experimenting with Lisphp at Ning, and we have some custom functions that seem to be helpful:

  • import: imports a lisp file: (import 'foo/bar/baz.php')
  • php: runs a PHP function without having to import it: (php :htmlentities 'foo')
  • cons: prepends an item to an array: (cons 'strawberry' flavors)
  • hash: creates an array of key-value pairs: (hash 'key1' 'value1' 'key2' 'value2')
  • array-set: sets an item on a multidimensional array: $flavors['foo']['bar'] = 'baz' is (array-set flavors 'foo' 'bar' 'baz')
  • array-get: gets an item from a multidimensional array: $flavors['foo']['bar'] is (array-get flavors 'foo' 'bar')
  • arr: array_replace_recursive()
  • environment: this is the Lisp environment itself added as a variable, to allow you to check if a function exists: (exists-at? environment 'my-function')

Here is the code that we use to call Lisphp. Note that you have a choice of runFile() or runCode(); also note the custom functions. You may need to make some modifications to get this to run in your own environment:


 * Parses Lisphp files.
class XG_Lisp {

     * Executes a Lisp file using Lisphp.
     * @param string $path  the path to the lisp file
     * @param array $env  names and values to add to the environment;
     *                             see Lisphp_Environment
     * @return  the result
     * @see
    public function runFile($path, $env = []) {
        return $this->run(Lisphp_Program::load(NF_APP_BASE . '/' . $path), $env);

     * Executes Lisp code using Lisphp.
     * @param string $code  the Lisp code
     * @param array $env  names and values to add to the environment;
     *                             see Lisphp_Environment
     * @return  the result
    public function runCode($code, $env = []) {
        return $this->run(new Lisphp_Program($code), $env);

     * Executes a Lisp program using Lisphp.
     * @param Lisphp_Program $program  a Lisp program object
     * @param array $env  names and values to add to the environment;
     *                             see Lisphp_Environment
     * @return  the result
    protected function run($program, $env = []) {
        $environment = Lisphp_Environment::full();
        foreach ($env as $name => $value) {
            $environment[$name] = $value;
        $environment['B'] = B(); // XG_BaseService
        // Import a PHP file: (import 'lib/components/activity/lib/foo.lisp')
        $environment['import'] = new Lisphp_Runtime_PHPFunction(function ($path) use ($environment) {
            $program = Lisphp_Program::load(NF_APP_BASE . '/' . $path);
            return $program->execute($environment);
        // Prepends an element to a list
        $environment['cons'] = new Lisphp_Runtime_PHPFunction([$this, 'cons']);
        $environment['->$'] = new XG_Lisp_GetField;
        $environment['array-get'] = new Lisphp_Runtime_PHPFunction([$this, 'arrayGet']);
        $environment['array-set'] = new Lisphp_Runtime_PHPFunction([$this, 'arraySet']);
        $environment['hash'] = new Lisphp_Runtime_PHPFunction([$this, 'hash']);
        $environment['var_dump'] = new Lisphp_Runtime_PHPFunction('var_dump');
        $environment['arr'] = new Lisphp_Runtime_PHPFunction('array_replace_recursive');
        $environment['environment'] = $environment;
        return $program->execute($environment);

     * Prepends an element to a list.
     * @param mixed $item  the item to prepend
     * @param array|Lisphp_List $list  the list to prepend the item to
     * @return array|Lisphp_List  a new list
    public function cons($item, $list) {
        if (is_array($list)) {
            return array_merge([$item], $list);
        return new Lisphp_List(array_merge([$item], $list->getArrayCopy()));

     * Retrieves an item from a multidimensional array
     * @param array $array  the array to read from
     * @param string $key1  the first key
     * @param string $key2  the second key, etc.
     * @return mixed  the value at the given keys
    public function arrayGet() {
        $args = func_get_args();
        $result = array_shift($args);
        foreach ($args as $key) {
            if (!is_array($result)) {
                return null;
            if (!array_key_exists($key, $result)) {
                return null;
            $result = $result[$key];
        return $result;

     * Sets an item on a multidimensional array
     * @param array $array  the array to read from
     * @param string $key1  the first key
     * @param string $key2  the second key, etc.
     * @param string $value  the value to set
     * @return mixed  the new multidimensional array
    public function arraySet() {
        $args = func_get_args();
        $array = array_shift($args);
        $subtree = &$array;
        $value = array_pop($args);
        $lastKey = array_pop($args);
        foreach ($args as $key) {
            if (!array_key_exists($key, $subtree)) {
                $subtree[$key] = [];
            $subtree = &$subtree[$key];
        $subtree[$lastKey] = $value;
        return $array;

     * Converts a list into an array of key-value pairs.
     * @param string $key1  the first key
     * @param string $value1  the first value
     * @param string $key2  the second key
     * @param string $value2  the second value, etc.
     * @return array  the key-value pairs
    public function hash() {
        $args = func_get_args();
        $hash = [];
        for ($i = 0; $i < count($args); $i += 2) {
            $hash[$args[$i]] = $args[$i+1];
        return $hash;


Read more…

Lisphp at Ning - Introduction

At Ning, we are experimenting with Lisphp, which allows us to call Lisp from PHP and vice versa. It's like an oasis of functional programming in the midst of PHP.

There isn't much on the web on Lisphp, so I am writing some blog posts (in my Lisphp category) about my experiences with it.

First, some helpful resources:

You'll notice that Lisphp does not come with any documentation other than what is on the Github page. A list of all the functions is in the Environment.php file. Here are brief descriptions of what some of the functions do:

  • define - defines a function or global variable
  • let - sets local variables
  • let* - sets local variables - the definitions can refer to each other
  • setf! - sets a local variable in an "imperative" style: (setf! foo 5). For setting local variables, prefer let first, followed by let*, followed by setf!
  • lambda - creates an anonymous function
  • apply - applies a function to an array of arguments
  • list - creates a Lisphp list: (list) or (list 'a' 'b' 'c')
  • array - creates a PHP array: (array) or (array 'a' 'b' 'c')
  • do - executes code several times in a loop
  • car - returns the first item in a list: (car flavors)
  • cdr - returns the remaining items in the list (i.e., not the first one): (cdr flavors)
  • at - returns the value at the given key: (at flavors 'key')
  • set-at! - sets the value at the given key: (set-at! flavors 'key' 'value')
  • unset-at! - unsets the value at the given key: (unset-at! flavors 'key' 'value')
  • exists-at? - does isset() on the value at the given key: (exists-at? flavors 'key')
  • count - returns the number of items in the list: (count flavors)
  • map - applies a function to every item in a list
  • filter - filters out items from a list
  • fold - (aka "reduce") goes through a list to create a single value
  • if - if statement
  • cond - switch statement
  • = - ==
  • == - ===
  • !=, !==, <, >, <=, >=, +, -, /, *, %, not, and, or, nil, true, false
  • . - concatenates strings
  • isa? - returns whether the object is an instance of the given class: (isa? foo <ArrayObject>)
  • string - strval()
  • substring - substr()
  • string-upcase - strtoupper()
  • string-downcase - strtolower()

I'm not sure about the following - if you know, let me know:

  • eval
  • quote
  • symbol
  • macro
  • dict - I'm not exactly sure how this works. I made a replacement called "hash"—see below.

I also added the following custom functions - I'll give the code in my next blog post:

  • import: imports a lisp file: (import 'foo/bar/baz.php')
  • php: runs a PHP function without having to import it: (php :htmlentities 'foo')
  • cons: prepends an item to an array: (cons 'strawberry' flavors)
  • hash: creates an array of key-value pairs: (hash 'key1' 'value1' 'key2' 'value2')
  • array-set: sets an item on a multidimensional array: $flavors['foo']['bar'] = 'baz' is (array-set flavors 'foo' 'bar' 'baz')
  • array-get: gets an item from a multidimensional array: $flavors['foo']['bar'] is (array-get flavors 'foo' 'bar')
  • arr: array_replace_recursive()
  • environment: this is the Lisp environment itself added as a variable, to allow you to check if a function exists: (exists-at? environment 'my-function')

Finally, some tips:

  • See if your editor has a plugin that will automatically indent your Lisp code. For example, Sublime Text has a lispindent plugin that will indent your code whenever you press Enter; you can also press Command+I to re-indent the selected code.
  • Sometimes you may need to dive into the Lisphp code to fix things. This is a good opportunity to learn how Lisphp works, and to contribute back by submitting a pull requests. I submitted two pull requests and they were accepted immediately.
  • To import a constant: (use +XG_Model::PLAINTEXT+). Now you can reference +XG_Model::PLAINTEXT+.
Read more…

Sublime Text Window-Splitting Bliss

I'm trying out Sublime Text again, and I'm really missing how easy it is to split windows in jEdit. In jEdit, you just do:

  • Command+2 to split the window horizontally.
  • Command+3 to split the window vertically.
  • Command+0 to unsplit the window.

Well, it turns out you can do this in Sublime Text! First, install the Origami plugin. Then add the following keybindings:

    { "keys": ["super+0"], "command": "destroy_pane", "args": {"direction": "self"} },
    { "keys": ["super+2"], "command": "create_pane_with_cloned_file", "args": {"direction": "right"} },
    { "keys": ["super+3"], "command": "create_pane_with_cloned_file", "args": {"direction": "down"} }

Now you can split your windows super-easily. Origami's default keybindings are pretty hard to remember and use, but the above is all you need.

Read more…

How I am writing commit messages now

I am trying a new way of writing commit messages based on Thoughtbot's 5 Useful Tips For A Better Commit Message. Basically my commit messages now look like this:

[Ticket number]: Summary: 1-line description: what and why

Problem: ...

Solution: ...

Side-effects: ...


[BDZL-4222] Summary: Digest's link "To control which emails you receive" is incorrect

Problem: The digest core has a link to /profiles/profile/emailSettings. However, this link is the old Bazel URL; in Bedazzle, the URL has changed to /main/profilesettings/email.

Solution: Make /profiles/profile/emailSettings redirect to /main/profilesettings/email.

Side effects: /profiles/profile/emailSettings responds with a 302 to /main/profilesettings/email, instead of a 404.

We'll see how long I can keep this up. The quality of these commit messages is great though - they give the why and the how of your commit.

Read more…

JWZ on commenting code

I love this exchange between Peter Seibel and Jamie Zawinski from the book Coders at Work. It captures what I like about well-documented code.

Seibel: Earlier you said something about writing code in order to make it easier to read, which ties into maintenance. What are the characteristics that make code easier to read?

Zawinski: Well, comments obviously. Writing down what the assumptions are and what this does. If it's building up a data structure, describing the layout of it. A lot of times I find that pretty helpful. Especially in writing Perl code when it's like, uh, well, it's a hash table and values are bunch of references to lists, because the data structures in Perl are just nuts. Do I need a right arrow here to get to this? I find examples like that to be helpful.

I always wish people would comment more, though the thing that makes me cringe is when the comment is the name of the function rephrased. Function's called push_stack and the comment says, This pushes to the stack. Thank you.

You've got to say in the comment something that's not there already. What's it for? Either a higher-level or a lower-level description, depending on what's most important. Sometimes the most important thing is, what is this for? Why would I use it? And sometimes the most important thing is, what's the range of inputs that this expects?

Long variable names. I m not a fan of Hungarian notation, but I think using actual English words to describe things, except for loop iterators, where it's obvious. Just as much verbosity as possible, I guess.

Read more…

Edward Feser's road from atheism to theism

I was reading a blog post by philosophy professor Edward Feser describing his conversion from atheism to theism. It is interesting to note the reasons that led to his change of mind:

  • "The first of them had to do instead with the philosophy of language and logic. . . As the arguments sank in over the course of months and years, I came to see that existing naturalistic accounts of language and meaning were no good."
  • "At first, and like so many undergraduate philosophy majors, I took the materialist line for granted.  Mental activity was just brain activity.  What could be more obvious?  But reading John Searle’s The Rediscovery of the Mind destroyed this illusion, and convinced me that the standard materialist theories were all hopeless."
  • "Most importantly, though, Lockwood’s book introduced me to Bertrand Russell’s later views on these issues, which would have a major influence on my thinking ever afterward.  Russell emphasized that physics really gives us very little knowledge of the material world.  In particular, it gives us knowledge of its abstract structure, of what can be captured in equations and the like.  But it gives us no knowledge of the intrinsic nature of matter, of the concrete reality that fleshes out the abstract structure.  Introspection, by contrast, gives us direct knowledge of our thoughts and experiences.  The upshot is that it is matter, and not mind, that is the really problematic side of the mind-body problem."
  • "Second, a complete naturalistic explanation of intentionality is impossible."
  • "It was also while still a naturalist that I first started to take a serious interest in Aristotelianism, though at the time that interest had to do with ethics rather than metaphysics. . . One consequence of this was that I always took teleology seriously, because it was so clearly evident a feature of ordinary practical reasoning."
  • "As I argue in The Last Superstition, many of the so-called “traditional” problems of philosophy are really just artifacts of the anti-Scholastic revolution of the moderns."
  • "Fregean and related arguments had gotten me to take very seriously the idea that something like Platonic realism might be true.  (I would later see that Aristotelian realism was in fact the right way to go, but the basic anti-naturalistic move had been made.)  The arguments of Searle and others had shown that existing versions of materialism were no good.  Russellian arguments had shown that modern science and philosophy had no clear idea of what matter was in the first place.  Whatever it was supposed to be, though, it seemed it was not something to which one could assimilate mind, at least not if one wanted to avoid panpsychism.  Naturalism came to seem mysterious at best.  Meanwhile, Aristotelian ideas had a certain plausibility.  All that was needed was some systematic alternative to naturalism."
  • "Then there was Aquinas. . . It was all very strange.  Aquinas’s arguments had a certain power when all of this metaphysical background was taken account of.  And there was a certain plausibility to the metaphysics."
  • "The only reason for not taking Aquinas and similar thinkers seriously seemed to be that most other academic philosophers weren’t taking them seriously.  And yet as I had come to learn, many of them didn’t even understand Aquinas and Co. in the first place, and their own naturalism was riddled with problems."
  • "As I taught and thought about the arguments for God’s existence, and in particular the cosmological argument, I went from thinking “These arguments are no good” to thinking “These arguments are a little better than they are given credit for” and then to “These arguments are actually kind of interesting.”  Eventually it hit me: “Oh my goodness, these arguments are right after all!”"
Read more…

Favorite things to do

If you ever wonder what your favorite things to do are, or what's the best way to spend your leisure time, may I offer these wonderful suggestions taken from an unlikely source—the section on the Sunday rest from the Catechism of the Catholic Church:

  • Family. “Christians will also sanctify Sunday by devoting time and care to their families and relatives, often difficult to do on other days of the week.”
  • Good works. “Sunday is traditionally consecrated by Christian piety to good works and humble service of the sick, the infirm, and the elderly.”
  • Prayer. “On Sundays and other holy days of obligation, the faithful are to refrain from engaging in work or activities that hinder the worship owed to God, the joy proper to the Lord's Day, the performance of the works of mercy, and the appropriate relaxation of mind and body.”
  • Study. “Sunday is a time for reflection, silence, cultivation of the mind, and meditation which furthers the growth of the Christian interior life.

My personal favorite is Study. Here are some of the resources that I like to immerse myself in on those rare occasions that I have blocks of free time:

  • Music: Listen. Music appreciation course CDs and textbook.
  • Philosophy: Aristotle: the desire to understand.
  • Poetry: The New Penguin Book of English Verse. Unlike most poetry anthologies which are organized by author, this organizes the poems by date of composition. So it feels like a kind of time machine.
  • Religion: Introduction to the Devout Life. St. Francis de Sales.
  • Art: The Story of Art. Generously illustrated story of the artistic masterpieces of the Western world.
Read more…

Beautiful correspondences in the Christian religion

In Christianity, the following things are analogous. The correspondence is rather beautiful - so much so that it is one of the things that convinces me of the truth of Christianity:

  • A wedding feast; the banquet table.
  • The crucifixion of Christ; the cross.
  • Jewish sacrifice; the altar.
  • The Eucharist; the altar.
  • The marital act; the marital bed.

I'm sure someone could easily write a book on the beauty of the relationships between these things.

Read more…

Classic books on programming

Yesterday I spent too much time checking out the books in 10 Books That Will Substitute a Computer Science Degree. It was discussed on Hacker News. Many of the books come from the ACM's list of classic books. To see a description of the book, click the book's name on the ACM site.

Several of the books are out of print, but some are available freely online. The ACM also offers PDFs of several of the books for purchase, for about $20.

Read more…

Loving God like you love your spouse

Recently I asked my spiritual director, Sister Monica Kaufer (who is leaving Vancouver soon—boo), “What must I do to enter the kingdom of God?” She replied, “How do you love Mila?”

Following that line of thought, here are the main points from the excellent book The Seven Principles for Making Marriage Work and how each might apply to our relationship with God. It is interesting to note the similarities.

1. Enhance your love maps.

  • Marriage: Know the details of your spouse's preferences, history, thoughts, how the day went, etc.
  • God: Know about God through the Bible, spiritual reading, doctrine, etc.

2. Nurture your fondness and appreciation.

  • Marriage: Mediate on your spouse and what makes you cherish him or her.
  • God: Words or songs of praise.

3. Turn toward each other instead of away.

  • Marriage: Listen to your spouse when he or she makes bids on your attention.
  • God: Listen to how God is leading you (through the Bible, events in your life, in the silence of prayer, etc.).

4. Let your partner influence you.

  • Marriage: Accept influence.
  • God: Obedience (to the Ten Commandments, the teachings of Jesus, etc.).

5. Solve your solvable problems [specific problems, not tied to your spouse's character].

  • Marriage: Compromise.
  • God: Prayer (supplication).

6. Overcome gridlock [deep conflicts of character with your spouse].

  • Marriage: Cope with it, like a bad back.
  • God: (Not sure. I guess this is equivalent to St. John of the Cross's "dark night of the soul", or Job's lament.)

7. Create shared meaning.

  • Marriage: "create an atmosphere that encourages each person to talk honestly about his or her convictions"—rituals, roles, goals, symbols.
  • God: Discern your vocation, go on retreats, etc.
Read more…

Perfectionism is like addiction

In both perfectionism and addiction, you can't get enough. You try, and you try, and you try, but you can't get no satisfaction.

I learned on Saturday (from a Theology of the Body conference) that an addiction is a tragic expression of a person's longing for the infinite. He looks for satisfaction in drugs, in pornography, in gambling - finite things - but they do not satisfy because he is really looking for an infinite thing, that is, God.

The same could be said, I think, of the perfectionist. He is looking for perfection in finite things (whether it be a text editor, or decorating his home, or following licensing rules, or whatever), and they fall short. What his heart is really looking for is an infinite thing, and that is God.

The conference said that there are stoics, addicts, and mystics; and that addicts are closer to mystics than stoics are.

Read more…

V1 and V2 engineers

In Kate Matsudaira's talk on Really Scaling a Rails Application, she makes an interesting distinction between V1 and V2 engineers. V1 engineers hack things together quickly. V2 engineers are slow and careful, writing documentation and unit tests. So I guess I'm a V2 engineer, and I used to get annoyed with the practice of hacking things together quickly. But seen in this light, I can see how V1 engineers have their place.

Read more…

Thoughts on Time

I have been so bothered recently about where my time goes. There is so much on my personal to-do list, and I seem to rarely have time to attack it. I talked over the problem with my wife, and I have the following thoughts.

It seems that there are 3 types of things that take up your time:

  • Events - stuff you put on your calendar.
  • Actions - stuff on your to-do lists.
  • Growth - a daily half-hour that you try to protect: time for mental growth (reading), physical growth (exercise), or spiritual growth (prayer). This is the time for "sharpening the saw" as Stephen Covey puts it.

It seems the only things that ever get done are the events - the stuff you put on your calendar. Meanwhile, your todo list continues to lengthen, and those growth tasks? Forget about it.

Like everyone else, you have 24 hours in a day. Where do they go? For me, roughly 8 hours goes to sleep, 8 hours to work, and 4.5 hours to eating/showering/brushing teeth/etc. So that leaves 3.5 hours as a theoretical maximum for taking care of events, actions, and growth. I'm lucky that I don't have to commute to work - if I did, I would probably only have 1.5 hours available for this personal stuff.

If you have an event scheduled for the evening, that wipes out my 3.5 hours. Events are sucky - you have to do them, but they wipe out your free time for the evening. Not much you can do about that...

...other than trying really, really hard to protect your half-hour of growth time. Even if you have an event for the evening, like going to a concert, really try to make 30 minutes for doing one of your growth activities. At least I'm going to try hard to do this. But sometimes, the event really takes the whole evening, and you can't do your growth thing. Too bad, so sad.

So now we have a plan for taking care of events and growth. When do we take care of our to-do list (actions)? Set aside a protective bubble of an afternoon or an evening with nothing scheduled - this is the time that you can fill with stuff on your to-do list. For me, I can't do this more than once a week, it seems, but I should be able to set aside such time once.

Life is crazy busy for everyone. This is how I'm going to protect time for both the important and the mundane. Because even the mundane stuff needs time to be done.

Read more…

Sometimes DuckDuckGo sucks

I've been using DuckDuckGo as my main search engine. But man, sometimes it sucks compared to Google.

For example, if I search DuckDuckGo for pope francis interview, it doesn't even find the page.

On Google, it is search result #1.

Here's how the search engines fared:

  • DuckDuckGo: Not found.
  • Google: Result #1.
  • Yandex: Not found.
  • Yahoo: Not found.
  • Bing: Not found.
  • Blekko: Not found.
  • Ask/Teoma: Result #1. I wonder if it uses Google.
  • AOL: Result #1. Uses Google.
  • Lycos: Not found.
  • Dogpile: Not found.
  • Excite: Not found.
  • Mahalo: Not found.
  • Yippy/Clusty: Not found.
Read more…

Visualizing a 4D hypersphere

My coworker Chris Brewer created a visualization that allows you to experience a 4D sphere (or "hypersphere") by zooming around a 3D space.

Set Viewing Distance to 1000 and choose Map 1.

What appears to be a spherical container is actually the "equator" of the hypersphere. The three spokes are meridians (lines of longitude), and they meet at one of the poles. Mind-blowing.

9:44:27 AM jona: so tell me what this is exactly - it seems that I'm inside a sphere that gets distorted as I move around it
9:45:00 AM BrewerC1: Sure, so do you know what a hypersphere or 4 dimensional sphere is?
9:45:38 AM jona: is that something where x^3 + y^3 + z^3 + a^3 = 5?
9:45:49 AM BrewerC1: Yeah, exactly like that
9:45:52 AM BrewerC1: er except
9:45:55 AM BrewerC1: squared
9:45:57 AM jona: ok
9:46:29 AM jona: how does the x, y, z that I'm moving around relate to that equation's x, y, z, and a?
9:46:58 AM BrewerC1: so in this spaces, you are moving through 4d space, but with the constraint that you are always R^2 units from the origin of the space
9:47:06 AM BrewerC1: which confines you to the surface of the hypersphere
9:47:11 AM jona: ok
9:47:19 AM BrewerC1: So the surface of a sphere is a 2d space
9:47:25 AM BrewerC1: and the surface of a 4d sphere is a 3d space

Read more…

Did you know?

I'm a software engineering consultant. This means I can help your company with your software engineering needs:

  • providing temporary manpower for short-staffed software projects

  • helping new software projects get off to a good architectural start

  • improving the performance and reliability of old, legacy software systems

  • doing an important investigation or small project that you've always wanted to do but haven't had time for

Since 1999, I have done software engineering projects for the Canadian government, for Silicon Valley startups, and for established Bay Area companies, for small companies and medium-sized companies, for successful commercial projects and open-source projects. 

Currently accepting small projects. If you have one, email or call me.