We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.

Nonstraightforward

From Esolang
Jump to navigation Jump to search
This article is not detailed enough and needs to be expanded. Please help us by adding some more information.
This is still a work in progress. It may be changed in the future.

Nonstraightforward is an esolang by User:RaiseAfloppaFan3925 in 2025 based off of Yappacino that is a subset of JavaScript with the unavailable features supported only through uselessly verbose syntax.

Nonstraightforward
Paradigm(s) imperative, procedural, declarative, object-oriented (prototype-based)
Designed by User:RaiseAfloppaFan3925
Appeared in 2025
Type system dynamic, fake
Memory system variable-based
Computational class Turing-complete
Reference implementation Nonstraightforward
Influenced by ES6, TypeScript, Yappacino
File extension(s) .nstrfwd

Expressions

For Booleans affirmative and negative. (for true and false respectively) are the primary supported literals — however true and false are supported too.

All operators in JavaScript are supported, however Lua's string concatenation operator .. as well as an augmented assignment version ..= are supported as well. They are intended to only be used with strings.

Nonstraightforward doesn't have function expressions, but it does have subroutine expressions. Subroutine expressions have the form subroutine (params) { body } and may have the synchronous/async/asynchronous modifiers.

Statements

Unlike JavaScript, Nonstraightforward requires that there be semicolons at the end of every statement.

Declarations

Classifications

Nonstraightforward lacks classes, but it does have classifications. They are defined with the classification keyword.

Classifications can have one or no constructor, defined with the same syntax as in JavaScript. Methods however, have modifiers and require the function/subroutine keyword.

classification Cycle {
    construct() { ~/usr/bin/this/pair = null; }
    public radioactive immutable readonly synchronous subroutine bind_pair(other) {
        ~/usr/bin/other/pair = ~/usr/bin/this;
        ~/usr/bin/this/pair = ~/usr/bin/other;
    }
}

Classifications can be instantiated with either new or construct.

Functions

Functions use the function keyword and can also use async/asynchronous and synchronous. They can also use modifiers. Default values for parameters are supported, but object destructuring and ... are not.

Subroutines

Subroutines use the subroutine keyword and can use modifiers and async/asynchronous and synchronous. Unlike functions though, they can have type annotations. Default values for parameters are supported, but object destructuring and ... are not.

Variables

The preferred way to declare variables is with variable and constant which map to let (ironic since var is right there, but var has cursed semantics because JavaScript was initially hacked together in 10 days) and const respectively. However, let/const/var are supported too.

Modifiers

Declarations have modifiers which modify their visibility outside the module, their stability, and their mutability. Modifiers may only be applied in the order of visibility–stability–mutability–read/write permissions.[1]

The visibility modifiers are private and public. If the visibility modifier is public, then default and nondefault may be specified.

The stability modifiers are, in order from most stable to most unstable — stable, unstable, volatile, hazardous, biohazard, and radioactive — with stable being the default.[2]

The mutability specifiers are mutable and immutable, the latter of which is equivalent to const. By default, this modifier is mutable.

Since const in JavaScript only makes the REFERENCE to the value constant and not the value itself, you can still mutate an immutable variable, as shown below.[3]

immutable readwrite constant x = { pi: 3, e: 3, tau: 7 };
// x = { pi: 3.14, e: 2.718, tau: 6.28 }; // this would throw a runtime error
~/dev/stdout/write/ln(x); // { a: 3, b: 6 }
x.pi = 3.14;
x.e = 2.718;
x.tau = 6.28;
~/dev/stdout/write/ln(x); // { pi: 3.14, e: 2.718, tau: 6.28 }

The read/write permission specifiers are readonly, readwrite, and writeonly. readonly makes it so that the VALUE pointed to by a variable is immutable, while the reference can still be changed to point to another value.[3] readonly in the reference implementation compiles to a call to Object.freeze. It should also call Object.freeze every time a readonly variable is assigned to, however as of C9 2026.05.23, this is not implemented. writeonly is supposed to make reading the value illegal, however as of C9 2026.05.23, that is not implemented yet too. By default, this modifier is readwrite.

Below is another example of the unintuitive behavior of the mutability and RW permission modifiers.[3]

mutable readonly variable x = { pi: 4 };
x.pi = 3.14;
console.log(x); // { pi: 4 }
x = { pi: 3.14 };
console.log(x); // { pi: 3.14 }

Control flow

Conditional statement

A conditional statement is of the form stipulate that the provision [condition] qualifies [body] and else is done with otherwise.

stipulate that the provision x === "1" qualifies {
    ~/dev/stdout/write/ln("one.");
} otherwise stipulate that the provision x == "1" qualifies {
    ~/dev/stdout/write/ln("1 or \"1\"?");
} otherwise {
    ~/dev/stdout/write/ln("idk bro");
}

Nonstraightforward also has JavaScript's if/else statement.

Exceptions

A value can be thrown as an exception using the throw or retaliate keyword.

Pattern matching

switch statements are in Nonstraightforward.

Return

Returning from a function can be done with the normal return or the reciprocate statement.

Looping

Currently, Nonstraightforward only has while loops. The preferred way to do looping is with so long as the condition <condition> is still upholding, then do [body]. However, normal JavaScript while (condition) [body] and do [body] while (condition) are supported too.

Terminating a loop or branch of a switch statement can be done with terminate or break, and skipping to the next iteration of a loop can be done with continue.

Modules

Nonstraightforward can import modules using an ES-style import of the form from module "module" request { imports };. Default imports and everything as alias imports (wildcard imports) are supported, but not string literal imports or alias imports.

.nstrfwd imports are supported and are automatically converted into .nstrfwd.js in code generation in the reference implementation.

SYSROOT

~/, which is actually named SYSROOT, is a constant implicitly declared at the start of a file. It has the members dev.stdout, dev.stderr, dev.random, dev.urandom, and dev.zero. These all compile to a SYSROOT member access, for example ~/dev/stdout compiles to SYSROOT.dev.stdout.

Output

~/dev/stdout and ~/dev/stderr each have a member write.ln which holds console.log and console.error respectfully.

Random

~/dev/random and ~/dev/urandom are getters that return the result of Math.random().

~/dev/zero

~/dev/zero is just a getter that always returns the number zero.

~/usr/bin

~/usr/bin is a member access expression that requires a variable name after it. Despite syntactically using SYSROOT, it does not access SYSROOT at all. For example, ~/usr/bin/Math/abs compiles to Math.abs. This means that ~/usr/bin/variable is not the same as SYSROOT.variable.

Examples

"Hello, world!" program

~/dev/stdout/write/ln("Hello, world!");

References