Nonstraightforward
Nonstraightforward is an esolang by User:RaiseAfloppaFan3925 based off of Yappacino that extends JavaScript with uselessly verbose syntax.
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 | Yappacino, TypeScript |
File extension(s) | .cvsc (legacy), .nstrfwd |
Types
Like Yappacino, Nonstraightforward has fake types. The types can be anything, since the parser does not care and parses only one token as the type.
Types are required in both variable and function declarations, unlike Yappacino which only requires types in function declarations.
// This is the type | // V public nondefault stable immutable readonly label number: PI <- 3.14159;
Access specifiers
Nonstraightforward brings to the table some access specifiers, which have an actual effect.
For example, a public declaration looks like:
public default stable immutable readonly synchronous subroutine: number <- abs(x --> number) { return x; } public nondefault stable mutable readwrite label number: counter <- 0;
The default
keyword marks the declaration as a default export, which when compiled to JavaScript results in this:
// CommonJS function abs(x) { return x; } module.exports.default["abs"] = abs; // ES module export default function abs(x) { return x; }
Likewise, a private declaration can be declared with the private
keyword instead.
private stable immutable readonly synchronous subroutine: number <- add(x, y --> number, number) { return x + y; } private stable mutable readwrite label number: privateCounter <- 0;
Immutability
Nonstraightforward has "tiers" or "levels" of immutability, specifically 4 levels of immutability. These are controlled with the following access specifiers.
mutable/immutable readonly/readwrite/writeonly
mutable
and immutable
A mutable
variable is a normal JavaScript variable declared with let
, while a immutable
variable or function will be declared with const
.
immutable
for functions does nothing right now, however that might change in the future.
readonly
and readwrite
and writeonly
A readonly
variable or function will have Object.freeze()
called on it, making it impossible to modify the object. However, this does NOT stop the variable itself from being modified.
private stable mutable readonly label object: x <- { x: 1, y: 2 }; x.x = 2; // does nothing because of Object.freeze() x = { x: 3, y: 8 }; // works because x is mutable
A readwrite
variable is a normal variable, with no special rules applied to it.
The writeonly
access specifier does absolutely nothing as of now, but it is intended to make the variable impossible to read.
Tiers of Immutability
The four tiers of immutability mentioned earlier are based on immutability of the reference and immutability of the variable.
Tier | Reference | Value | Access Specifiers | Comment |
---|---|---|---|---|
Tier 1 | Mutable | Mutable | mutable readwrite
|
Typical variable declaration in JavaScript |
Tier 2 | Mutable | Immutable | mutable readonly
|
The variable can be modified, but the value cannot be modified |
Tier 3 | Immutable | Mutable | immutable readwrite
|
Same as JavaScript's const , reference is constant but value is mutable
|
Tier 4 | Immutable | Immutable | immutable readonly
|
True immutability. The value of the variable cannot be modified, and the variable cannot be changed to reference another object. |
Variables
A variable in Nonstraightforward can be declared like so:
// for public variables public default/nondefault mutable/immutable readonly/readwrite/writeonly label type: name <- value; // for private variables private mutable/immutable readonly/readwrite/writeonly label type: name <- value;
Examples:
public nondefault immutable readonly label number: PI <- 3.14159265358979; private mutable readwrite label string: message <- "Hello, World!"
Functions
Functions, or subroutines in Nonstraightforward, can be declared like so:
// remember that you can have any number of parameters // but it is a compile error for the number of types and // number of parameters to not be equal // for public subroutines public default/nondefault mutable/immutable readonly/readwrite/writeonly asynchronous/synchronous subroutine: type <- name(arg1, arg2, arg3 --> type1, type2, type3) // for private subroutines private mutable/immutable readonly/readwrite/writeonly asynchronous/synchronous subroutine: type <- name(arg1, arg2, arg3 --> type1, type2, type3)
Example:
public nondefault immutable readonly synchronous subroutine: vec2 <- vec2_add(a, b --> vec2, vec2) { return { x: a.x + b.x, y: a.y + b.y }; }
Control flow
Nonstraightforward has some new control flow syntax made extremely long to rival Yappacino.
If/else statements
An if statement looks like this:
stipulate that the provision [condition] qualifies [body]
And the else
keyword is replaced by otherwise
.
Example:
stipulate that the provision x == 7 qualifies { ~/dev/stdout/write/ln("2763"); } otherwise stipulate that the provision x == 2763 qualifies { ~/dev/stdout/write/ln("That number seems familiar"); } otherwise { ~/dev/stdout/write/ln("I don't know that number."); }
Parentheses surrounding the condition are optional.
While loops
A while loop looks like this:
so long as the condition [condition] is still upholding, then do [body]
JavaScript | Nonstraightforward |
---|---|
break
|
terminate
|
continue
|
continue (as of C8)
|
Example:
private stable mutable readwrite label number: x <- 1; so long as the condition x <= 10 is still upholding, then do { ~/dev/stdout/write/ln(`x = ${x}`); x++; } ~/dev/stdout/write/ln("Finished!");
For loop
Currently, Nonstraightforward has one kind of for loop, which loops over a range of numbers.
for all values of [variable] in the range of numbers from [start] to [end] do [body]
This is an exclusive loop, it compiles to
for (let [variable] = [start]; [variable] < [end]; [variable]++) [body]
Example:
for all values of i in the range of numbers from 0 to 10 do { ~/dev/stdout/write/ln(`i = ${i}`); }
Currently, decrement loops are not supported.
The SYSROOT
variable
Like Yappacino, member accesses in Nonstraightforward are done with file paths. However, Nonstraightforward uses POSIX file paths, to contrast Yappacino's usage of Windows file paths.
To print something in Nonstraightforward, this function is used
~/dev/stdout/write/ln()
This compiles to
SYSROOT.dev.stdout.write.ln()
Likewise, member accesses are done with
~/dev/scope/object/member1/member2/member3
And it compiles to
object.member1.member2.member3
Importing modules
To import a declaration from a module, the following syntax is used:
from module "module" request thing; // for default declarations from module "module" request { otherThing }; // for non-default declarations
And remember, Nonstraightforward compiles down to JavaScript, so this works ONLY with JavaScript/TypeScript modules.
Also, this syntax only compiles to ES imports, so for CommonJS imports, use require()
instead.
Interfaces
Like TypeScript, Nonstraightforward supports interfaces. Interfaces in Nonstraightforward do nothing like the rest of the types, however they can serve as a guide for type layouts.
Example:
public nondefault stable object structure member layout Vector2 { stable member x <= number; stable member y <= number; }