Re: Factor

Factor: the language, the theory, and the practice.

Sequence Case

Sunday, September 10, 2023

#macros #syntax

Some languages allow ranges to be used in switch statements. For example, like in this Swift code:

let count = 3_000_000_000_000
let countedThings = "stars in the Milky Way"
var naturalCount: String
switch count {
case 0:
    naturalCount = "no"
case 1...3:
    naturalCount = "a few"
case 4...9:
    naturalCount = "several"
case 10...99:
    naturalCount = "tens of"
case 100...999:
    naturalCount = "hundreds of"
case 1000...999_999:
    naturalCount = "thousands of"
default:
    naturalCount = "millions and millions of"
}
println("There are \(naturalCount) \(countedThings).")

Let’s build this functionality in Factor!

Range Syntax

First, let’s look at how we can construct a range.

1000 999,999 [a..b]

If we wanted to use that in a case statement, we could wrap it with a literal:

{ $[ 1000 999,999 [a..b] ] [ "thousands of" ] }

But that’s not that elegant, instead let’s define a syntax word to construct our range:

SYNTAX: ..= dup pop scan-object [a..b] suffix! ;

This is much cleaner:

{ 1000 ..= 999,999 [ "thousands of" ] }

Combinators

In Factor, we have combinators which are, at some level, just words that take code as input. We sometimes refer to these as higher-level concepts, but they allow us to be more expressive with fewer tokens.

Let’s build our combinator using a macro to transform some input cases:

MACRO: sequence-case ( assoc -- quot )
    [
        dup callable? [
            [ first dup set? [ in? ] [ = ] ? '[ dup _ @ ] ]
            [ second '[ drop @ ] ] bi 2array
        ] unless
    ] map [ cond ] curry ;

Now we can write a Factor version of the original Swift example above:

3,000,000,000,000 {
    { 0 [ "no" ] }
    { 1 ..= 3 [ "a few" ] }
    { 4 ..= 9 [ "several" ] }
    { 10 ..= 99 [ "tens of" ] }
    { 100 ..= 999 [ "hundreds of" ] }
    { 1000 ..= 999,999 [ "thousands of" ] }
    [ drop "millions and millions of" ]
} sequence-case "There are %s stars in the Milky Way.\n" printf

Cool!

This is available in the combinators.extras vocabulary in the development version of Factor.