paint-brush
Knowing 'When' to Switch: A Glimpse into the Future of Pattern Matchingby@jordanbunke
121 reads New Story

Knowing 'When' to Switch: A Glimpse into the Future of Pattern Matching

by Jordan Bunke
Jordan Bunke HackerNoon profile picture

Jordan Bunke

@jordanbunke

I'm currently working on Stipple Effect, a pixel art editor...

January 28th, 2025
Read on Terminal Reader
Read this story in a terminal
Print this story
Read this story w/o Javascript
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

An overview of the when statement: a flexible pattern matching structure in DeltaScript, and an alternative to switch
featured image - Knowing 'When' to Switch: A Glimpse into the Future of Pattern Matching
1x
Read by Dr. One voice-avatar

Listen to this story

Jordan Bunke HackerNoon profile picture
Jordan Bunke

Jordan Bunke

@jordanbunke

I'm currently working on Stipple Effect, a pixel art editor with animation capabilities and a rich scripting ecosystem.

Learn More
LEARN MORE ABOUT @JORDANBUNKE'S
EXPERTISE AND PLACE ON THE INTERNET.
0-item

STORY’S CREDIBILITY

Guide

Guide

Walkthroughs, tutorials, guides, and tips. This story will teach you how to do something new or how to do something better.

An overview of the when statement: a flexible pattern matching structure in DeltaScript, and an alternative to switch


I have spent over a year working obsessively to bring my vision of the perfect pixel art editor to life. The fruit of my labor is Stipple Effect, a program that lets users write scripts for a variety of use cases, including transforming the project for display in the preview window in real-time:


The rotating head animation is generated from the texture by a preview script

The rotating head animation is generated from the texture by a preview script


I was very particular about how I wanted the script-writing process to feel for users: quick, clear, painless, iterative. To achieve that, I designed and implemented my own scripting language rather than embedding Lua or another established scripting language in my program.


The result is DeltaScript, a scripting language "sketelon" designed to be extended for specific application domains. Stipple Effect's scripting API is one such extension.


I released the language specification for DeltaScript v0.1.0 last week (Jan 16, 2025), so I figured now is the perfect time to write about one of the features I am most excited about.


Note: This blog post is adapted from the original on my website. Read it there to see the proper syntax highlighting for DeltaScript code snippets.


Why when?

DeltaScript was always supposed to be a high-level interpreted language. As such, I wanted the language to have powerful, flexible control flow structures that could express complex logic concisely and still be readable and maintainable.


One of my biggest frustrations as a programmer is the limitations and the implementation philosophy of the traditional switch statement: limited to literals in case labels, fallthrough, etc.


I do most of my programming in Java, and I must say, recent Java language versions have drastically extended the functionality of switch and turned it into a near-perfect pattern-matching structure. I wanted to do something similar for DeltaScript.

How when works

My when statement supports three different kinds of non-trivial cases:

  • is - matches the control expression against one or more expressions, checking for equality
  • matches - uses the special identifier _ to replace the control expression and defines a pattern in the form of a boolean expression
  • passes - accepts a test function (predicate) of type (T -> bool), where T is the type of the control expression


These cases can be arranged inside a when statement in any order. Each case is checked in order until a case passes its check, at which point the case body is executed. There is no fallthrough; once the runtime execution identifies a successful match case and executes its body, the execution drops out of the when statement and executes the statement that follows it.


Consider this example:

(color c) {
  ~ string pfx = "The color is ";

  when (c) {
    matches _.alpha == 0 -> print(pfx + "transparent");
    is #000000 -> print(pfx + "black");
    is #ffffff -> print(pfx + "white");
    matches _.r == _.g && _.r == _.b && opaque(_) -> 
            print(pfx + "a shade of grey");
    is #ff0000, #00ff00, #0000ff -> print(pfx + "an RGB primary color");
    passes ::bright_opaque -> print("bright");
    otherwise -> print(pfx + "not a match");
  }
}

bright_opaque(color c -> bool) {
  int max = max([ c.r, c.g, c.b ]);
  return max == 0xff && opaque(c);
}

opaque(color c -> bool) -> c.alpha == 0xff


This logic cannot be expressed by a traditional switch statement. Expressing it with an if...else if would look like this:


(color c) {
  ~ string pfx = "The color is ";

  if (c.alpha == 0) print(pfx + "transparent");
  else if (c == #000000) print(pfx + "black");
  else if (c == #ffffff) print(pfx + "white");
  else if (c.r == c.g && c.r == c.b && opaque(c))
    print(pfx + "a shade of grey");
  else if (c == #ff0000 || c == #00ff00 || c == #0000ff)
    print(pfx + "an RGB primary color");
  else if (bright_opaque(c)) print("bright");
  else print(pfx + "not a match");
}

bright_opaque(color c -> bool) {
  int max = max([ c.r, c.g, c.b ]);
  return max == 0xff && opaque(c);
}

opaque(color c -> bool) -> c.alpha == 0xff


You can read the full semantics of the when statement in the language specification.


I'll leave you with this long-form example that shows off a few additional language features of interest:


() {
    string[] words = [
        "Racecar", "Pilot", "Madam", 
        "Able was I ere I saw Elba", 
        "Nurses run", "Highway 61", 
        "A man, a plan, a canal - Panama"
    ];

    (string -> bool) no_whitespace_palindrome = 
                    (s -> palindrome(no_whitespace(s)));

    for (word in words) {
        when (word) {
            passes ::palindrome -> print("\"" + _ + "\" is a pure palindrome!");
            passes no_whitespace_palindrome -> 
                            print("\"" + _ + "\" is a palindrome if whitespace is ignored");
            passes (s -> palindrome(only_letters(s))) -> 
                            print("\"" + _ + "\" is a palindrome if whitespace and punctuation are ignored");
            otherwise -> print("\"" + _ + "\" is not a palindrome");
        }
    }
}

palindrome(string s -> bool) {
    string lc = lowercase(s);
    return lc == reverse(lc);
}

reverse(string s -> string) {
    string res = "";

    for (c in s)
        res = c + res;

    return res;
}

lowercase(string s -> string) {
    string res = "";

    for (c in s) {
        int unicode = (int) c;

        if (uppercase_letter(c))
            res += (char) ((int) 'a' + (unicode - (int) 'A'))
        else
            res += c;
    }

    return res;
}

no_whitespace(string s -> string) {
    ~ char{} WHITESPACE = { ' ', '\t' '\n' };
    string res = "";

    for (c in s)
        if (!WHITESPACE.has(c))
            res += c;
    
    return res;
}

only_letters(string s -> string) {
    string res = "";

    for (c in s)
        if (uppercase_letter(c) || lowercase_letter(c))
            res += c;
    
    return res;
}

uppercase_letter(char c -> bool) {
    int unicode = (int) c;
    return unicode >= (int) 'A' && unicode <= (int) 'Z';
}

lowercase_letter(char c -> bool) {
    int unicode = (int) c;
    return unicode >= (int) 'a' && unicode <= (int) 'z';
}


This script produces the following output:

"Racecar" is a pure palindrome!
"Pilot" is not a palindrome
"Madam" is a pure palindrome!
"Able was I ere I saw Elba" is a pure palindrome!
"Nurses run" is a palindrome if whitespace is ignored
"Highway 61" is not a palindrome
"A man, a plan, a canal - Panama" is a palindrome if whitespace and punctuation are ignored


L O A D I N G
. . . comments & more!

About Author

Jordan Bunke HackerNoon profile picture
Jordan Bunke@jordanbunke
I'm currently working on Stipple Effect, a pixel art editor with animation capabilities and a rich scripting ecosystem.

TOPICS

THIS ARTICLE WAS FEATURED IN...

Permanent on Arweave
Read on Terminal Reader
Read this story in a terminal
 Terminal
Read this story w/o Javascript
Read this story w/o Javascript
 Lite
Also published here
Hackernoon
Bsky
X REMOVE AD