Friday, February 1, 2013

Crafting Code in Factor

An interesting post about crafting code in Clojure discusses how Clojure the language encourages code that is "concise, easier to read, and easier to maintain". I've mentioned before how Factor encourages readability, but I thought I would show how an implementation of the author's task in Factor is quite simple.

The problem statement is to "convert the keys of two maps into a comma-separated string" for nicer error messages. The specific tasks outlined by the author are:

  • Extract all the keys from both maps
  • Remove any duplicates
  • Convert the keys to strings
  • Sort the strings into ascending order
  • Build and return one big string, by concatenating all the key strings, using ", " as a separator
  • Return "<none>" if both maps are empty

Implementation

This specification can be translated more or less directly into Factor:

: sorted-key-list ( assoc1 assoc2 -- string )
    [ keys ] bi@ append members [ "<none>" ] [
        [ present ] map natural-sort ", " join
    ] if-empty ;

Testing

And some unit tests to show it works:

{ "<none>" } [ H{ } H{ } sorted-key-list ] unit-test

{ "barney, fred, wilma" } [
    H{ { "fred" t } }
    H{ { "barney" t } { "wilma" t } }
    sorted-key-list
] unit-test

{ "fred" } [ H{ { "fred" t } } H{ } sorted-key-list ] unit-test

{ "barney, fred" } [
    H{ { "fred" t } }
    H{ { "fred" t } { "barney" t } }
    sorted-key-list
] unit-test

Errata

An improved version of the Clojure version allows a variable number of maps to be combined in this manner. To do so in Factor might use as a macro, or instead just change the stack effect to take a sequence of maps that are combined, something like this:

: sorted-key-list2 ( seq -- string )
    [ keys ] map concat members [ "<none>" ] [
        [ present ] map natural-sort ", " join
    ] if-empty ;

No comments: