Skip to content

Instantly share code, notes, and snippets.

@jbunke
Last active September 7, 2024 12:48
Show Gist options
  • Save jbunke/60d7b7ba9779f8a44e96f2735ddd460e to your computer and use it in GitHub Desktop.
Save jbunke/60d7b7ba9779f8a44e96f2735ddd460e to your computer and use it in GitHub Desktop.
Simple prime number checker script in DeltaScript that showcases new language features: `when` statement and lambda expressions
// Ignore .js file extension - only used so GitHub's JavaScript linter provides decent code highlighting
/*
This script accepts an integer `to_check` and returns true if and only if it is a prime number.
Note the use of a when statement, DeltaScript's answer to switch statements.
Cases in when statements take three forms:
1. `is` cases compare the control expression against an expression and execute their body if the values are equal
2. `passes` cases provide a predicate that the control expression is tested against.
For a control variable of type T, predicates are functions of type (T -> bool).
Predicates can be expressed as function pointers - ::function_name - or as lambda expressions
3. when statements can optionally include a final `otherwise` case, which is a universally true case that always
executes its body when reached.
Cases in when statements are checked sequentially as written. The control flow drops out of the statement once the body
of the first valid case has been executed.
*/
(int to_check -> bool) {
when (to_check) {
// predicate is a lambda expression
passes n -> n <= 0 -> {
print("Please provide a POSITIVE integer");
return false;
}
// equivalent to `else if (to_check == 1)`
is 1 -> return false;
// predicate is a function pointer
passes ::even -> return to_check == 2;
otherwise -> {
int factors = factors(to_check);
// #| is the length operator - can be called on collections (lists <>, arrays [], sets {}) and strings
bool prime = #|factors == 2;
if (!prime) {
string assembled = "";
// type declarations in foreach loops are optional, as element type can be inferred from the iterable
for (factor in factors)
assembled += (assembled == "" ? "" : ", ") + factor;
print("[ " + assembled + " ]");
}
return prime;
}
}
}
even(int n -> bool) -> n % 2 == 0
factors(int n -> int<>) {
// Declares an immutable list of integers `factors` and initializes it to an empty list of integers
// immutability (`~` or `final`) means that `factors` cannot be reassigned; however the existing list may be modified
~ int<> factors = <>;
int sq = 1;
while (sq * sq < n)
sq++;
for (int check = sq - (sq * sq == n ? 0 : 1); check > 0; check--)
if (n % check == 0) {
factors.add(check, 0);
// appends the complementary factor if it is != to factor `check`
if (n / check != check)
factors.add(n / check);
}
return factors;
}
@jbunke
Copy link
Author

jbunke commented Sep 7, 2024

Comparison

Minimal representation of prime determination logic using different control flow statements

1. As when

when (to_check) {
    passes n -> n <= 1 -> return false;
    passes ::even -> return to_check == 2;
    otherwise -> #|factors(to_check) == 2;
}

2. As if ... else

if (to_check <= 1)
    return false;
else if (even(to_check))
    return to_check == 2;

return #|factors(to_check) == 2;

Note that the omission of curly braces here is simply a matter of code style and personal preference.

The third case could also be expressed as an else branch.

when expression

Next I'd like to implement a when expression to make something like this possible...

return when (to_check) {
    passes n -> n <= 1 -> false;
    passes ::even -> to_check == 2;
    otherwise -> #|factors(to_check);
};

@jbunke
Copy link
Author

jbunke commented Sep 7, 2024

My main reason for preferring the when statement is that to_check is omitted from our conditions. All conditions are performing checks on the value of that variable, so it seems redundant to reference it repeatedly.

Let me know what you think of when in DeltaScript! I'd be curious to know what you think of...

  • the choice of keywords (when, is, passes, otherwise) - for example, I opted not to reuse else for otherwise
  • the syntax generally
  • the grammar

Links:

@jbunke
Copy link
Author

jbunke commented Sep 7, 2024

Script boiled down to core logic:

(int to_check -> bool) {
    when (to_check) {
        passes n -> n <= 1 -> return false;
        passes ::even -> return to_check == 2;
        otherwise -> return #|factors(to_check) == 2;
    }
}

even(int n -> bool) -> n % 2 == 0
    
factors(int n -> int<>) {
    ~ int<> factors = <>;
    int sq = 1;

    while (sq * sq < n)
        sq++;

    for (int check = sq - (sq * sq == n ? 0 : 1); check > 0; check--) {
        if (n % check == 0) {
            factors.add(check, 0);
            
            if (n / check != check)
                factors.add(n / check);
        }
    }

    return factors;
}

@jbunke
Copy link
Author

jbunke commented Sep 7, 2024

Additional example

has_area(image img -> bool) -> img.w > 0 && img.h > 0
transparent_tl_pixel(image img -> bool) -> has_area(img) && img.pixel(0, 0).alpha == 0

// ...

tl_pixel_or_bw(image img -> color) {
    when (img) {
        passes ::transparent_tl_pixel -> return #000000;
        passes ::has_area -> return img.pixel(0, 0);
        otherwise -> return #ffffff;
    }
}

// Yes, DeltaScript supports color hex code literals!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment