Sequence Case
Sunday, September 10, 2023
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.