First of all, the language name is read "betascript",because that "Β" is a uppercase Beta, which, incidentally, is identical to a uppercase latin alphabet "B".
With that established, ΒScript is a scripting language based around the concept of mathematical functions as a basic data type. it is an interpreted, optionally-semicolon-terminated, lexically-scoped, OO-supporting language.
By mathematical functions, we mean functions defined from a subset of R^{n} to a subset of R. In order to avoid confusion, this documentation will reference these variables as "functions", and normal programming language functions as "routines".
ΒScript variables are dynamically-typed, and can be declared with the "let" keyword. There are four primitive data types in ΒScript: functions (numbers are treated as constant functions), real sets, strings and logic values. There is also "nil", which represents the lack of a value. In order to print values, use the "print" keyword, and adding (with the '+' operator) strings to other primitive data values always returns a string, when you want to format things more properly. One special thing about the print keyword: the beginning of what will be printed must be on the same line as the print keyword, otherwise it causes an error. Something else that's important: ΒScript allows greek alphabet lower case and uppercase characters in identifiers, as well as latin alphabet ones. this means an identifier can start with [a-z], [A-Z], [α-ω], [Α-Ω], '_', and the rest of it can be that, plus [0-9].
let m = nil
let n
let x = 1.0
let string = "i am a string"
let otherString = string + " but a little longer"
let y = x + 5
print otherString
print
x + y //causes error
print x //prints x
+ y //causes error, because + requires a left operand
print x +
y //works fine, and prints x + y
since ΒScript uses three-valued logic, not boolean logic, i should warn you: Some operations will return unknown instead of true or false. Operating with unknown always returns unknown, regardless of the other value. Besides, there are unicode alternatives for the logical operators:
For sets, strings and booleans, one can use '==' or '===' interchangeably to check for equality. That isn't the case for functions, as will be explained later.
print "banan" == "banan" //prints true
print true === true //also prints true
Here is where ΒScript gets unique: functions defined from a subset of R^{n} to a subset of R are a data type. They are declared like normal variables with the "let" keyword, but with a catch: you can define a list of parameters after the variable name:
let f(x) = x
That 'x' after the variable name creates a special variable of type "Variable" (yes, i know it needs to change. For now, "Variable" is this special type which representes a function parameter, and "variable" is any variable). This data type is important later, when actually calling or derivating functions. It can have any valid variable name, which means it has to start with '_' or a letter and can contain '_', letters and numbers. Other than these parameter names, one may define functions with sums, subtractions, multiplications, divisions and exponentiations, as well as using number literals:
let f(x) = x
let g(x, y) = x + y
let h(x, y, z) = (x * y) ^ z
let i(x) = 6 * x ^ 2
you can also call these functions, of course. In order to do this, write the variable names, followed by the values you want for each parameter in the same order of the function declaration:
let f(x, y) = x ^ y
print f(2, 3)
outputs 8, but:
let f(y, x) = x ^ y
print f(2, 3)
Outputs 9.
of course, a system able to only create those functions would be almost useless. In order to create more complex functions, one uses function composition:
let f(x, y) = x ^ y
let g(x) = f(x, x)
The way to actually create all logarithmic, trigonometric, inverse trigonometric, hyperbolic, and inverse hyperbolic functions is actually by using function composition on native objects which implement these functions:
.let f(x, y) = sin(x) + ln(y)
print sin(1)
The complete list of functions that are already implemented is:
In some cases, the parameter list after a function name is optional. Since Variables end up declared in the local scope, the following code is perfectly valid, even if it is weird:
let f(x) = x ^ 2
let g(y) = 3 * y
let h = x * y
The main use for this is to allow defining functions with others easier. The following snippet works because of it:
let f(x) = x ^ 2
let g(y) = 3 * y
let h = f * g
This doesn't work, however, when a new variable needs to be used. If there is a single new variable in a function, one must list all of them, to make sure the variable order is as desired.
let f(x) = x ^ 2
let g(y) = 3 * y
let h(x, y, z) = f * g * arsinh(z)
ΒScript will always try to keep things as exact as possible. This means that sin(1) doesn't evaluate to 0.8414709848078965, but to sin(1). If you want approximate results, you need to use the '~' (or approximation) operator. Since we got into the subject of simplifications, i think it's a good spot to clarify something: there are A LOT of missing simplifications. What exists (for now) is there to make sure i could leave derivative rules as generic as possible without things printing weirdly. If you want to use something which would depend on a simplification, such as sin(pi/4) = 1/sqrt(2), check first.
print sin(1) //sin(1)
print ~sin(1) //0.8414709848078965
As i said before, there is a difference between == and === concerning functions. To simply evaluate whether two functions are exactly the same, returning a boolean value, use === (identically equal operator). You will never get a false positive or a false negative, but a lot of unknowns are likely. in essence, don't really trust the === operator unless you're using it with constant functions (numbers). To generate equations, which can be (for now) only used to create intentionally-defined sets, use ==. You can also generate inequations using <, >, ≤ and ≥. For constant functions, these operators will also generate boolean values instead of comparison objects.
let f(x) = sin ^ 2 (x)
let g(x) = e ^ x
let h(x, y) = sin(x) ^ 2 + cos(y) ^ 2
print h == 1 // false
print f === g // false
print f == g // f == g
print 2.0 == 3.0 // false
print 2.5 === 3.5 // false
ΒScript is able not only of evaluating functions, but calculating their partial derivatives of any order. For that, we use derivative expressions:
let f(x) = sin(x)
print del(f) / del(x) //cos(x)
print del(f) / del(x, x) //-sin(x)
print del(f) / del(x, x, x) //-cos(x)
print del(f) / del(x, x, x, x) //sin(x)
But, as i said, it calculates partial derivatives:
let f(x, y) = x ^ 2 * y ^ 3
print ∂(f) / ∂(x, y, x) //(6*((y)^(2)))
Besides, you can use ∂ as an alternative for 'del'. They are completely interchangeable.
This works for functions with any number of variables, and the result of the derivative expression is another function, which can be evaluated, derivated and used in composition just like any other! Also, for functions defined in a single variable, one can use the apostrophe shorthand:
let f(x) = x ^ 2
print f' === del(f) / del(x) //prints true;
ΒScript also allows users to define real sets. More specifically, intervals, roster sets, sets defined as solutions to equations, and sets derived from operations between the three former types. It is also able to compare sets for being disjoined with or contained in another, or to check if a particular number belongs to the set.
All set definitions can have the set keyword before them, but it is completely optional.
To define an interval, do exactly as you would when writing an interval! ( is a open left edge, and ) is a open right edge. [ is a closed left edge, and ] is a closed right edge. Separate the value of left and right edge with a comma. Note that infinity is a valid edge.
let A = set (10, 20];
let f(x) = 256 ^ x;
let B = [f(0), f(1));
Roster set is a fancy name for "set defined by listing all its members". In order to define them, put the elements in braces separated by commas.
let A = {10, 20, 30, 0, -127, pi, 318};
builder sets are sets defined as solutions to equations or equations systems. When betascript is sure that it can find all solutions to a equation, it will return another type of set instead of a proper builder set. For now, this only happens for linear equations. In order to declare them, between braces, put a optional list of parameters (which works exactly like the function declaration parameters, it defines the Variables and checks that they actually contain Variables when already defined), a |, them a equation or inequation (two functions to each side of ==, <, >, ≤ or ≥):
print {x| x > 5} //prints (5,∞) print {|x == 2} //prints { 2 }. Notice that we don't need to specify x because it was declared as a parameter in the previous line, but the '|' is always obligatory
the empty set is the set without any elements. it can be used in betascript as emptySet, ∅ empty braces ({}) or intervals with open edges where the edges are equal (anything of the form (a, a),[a, a) or (a, a]). It has the property of being the third value that is evaluated as false, together with nil, unknown and false.
betascript supports unions, complements, relative complements and absolute complements as operations that return sets, and it is able to check if two sets are disjoined, if one is contained in the other, or if a number belongs to a set. The "no false positives premise" applies here too. When it isn't able to compute one of the first operations to a Interval, Roster Set or Builder Set, it returns a composite set that works just like any other. Every operation has at least a keyword, operator or unicode operator associated with it:
let A = {1, 2, 3, 4}
let B = (0, 5)
let C = {x | x == 5}
print A // prints { 1, 2, 3, 4 }
print B //prints (0,5)
print C //prints { 5 }
print {| x ^ 2 == 2} //prints {x | ((x)^(2)) == 2} : betascript doesn't know how to solve quadratic equations yet
print A contained B //true
print 2.5 belongs A //false
print 2.5 ∈ B //true
print A union B //(0,5)
print A intersection B //{ 1, 2, 3, 4 }
print A disjoined C // true
print C' //(-∞,5) ∪ (5,∞)
print A \ B //∅
ΒScript supports the classics: C style if/else, while, and for statements.
If the condition inside the "if" parentheses is "truthy", the block following it is executed. If not, and there is an "else" statement following it, the block following it is executed. "Truthy" basically means "anything but nil, emptySet, false and unknown". By the way, boolean operations can be done with the "not", "and", and "or" keywords.
if (variable == nil) {
print "no value!"
} else if (variable < 0) {
print "variable is negative number!"
} else if (variable == 2 or variable == 3 or variable == 5 or variable == 7) {
print "variable is prime smaller than 10"
} else print "variable is something else"
Used for repetitions. While the condition between parentheses after "while" is "truthy", it will keep running the block after it. The first snippet below prints the integers from 0 to 9. The second one creates an endless loop, and the third one never runs.
var i = 0
while (i < 10) {
print i
i = i + 1
}
while (true) {
print "help!"
}
var v = 0
while (v == nil) {
print "Undefined!"
}
Syntactic sugar for a very common type of loop. It basically does the same as the first while example, but more compact.
for (var i = 0; i < 10; i = i + 1) print i
The first parenthesized expression is a variable declaration or assigment, and only runs once, before the first loop. The second expression is equivalent to the while condition, and the third one runs after every loop, but before the second expression is evaluated again.
Warning! the next section of documentation wasn't updated to version 0.3 - The Unicode Update!.Last update was to version 0.2 - Setting Up. This might mean this section is wrong, or just that the developer forgot to change the version in the html file.
ΒScript supports four types of comments, two of them classic, and two original creations.
Begins with "//". comments out all the code after // until the next linebreak.
//print "this will not print";
print "this will"; //print "but this will not";
print "this will be fine";
begins with a "/*", ends with "*/". Comments everything in between.
/*$this$is$a$multiline$comment$*/
begins with a "@" sign. Comments everything until the next whitespace, be it space, linebreak or tab.
print @this-is-a-name-comment-so-this-wont-print "but this will, because whitespace terminates them"
uses the "#" sign. In most cases, does exactly the same thing as the name comment, with the difference it can be used to set some interpreter flags, e.g. '#bs_tt_interpret' will make the interpreter go into twitter bot mode, which prohibits loops and routines and class definitions. Currently, this is the only directive which is defined, and there really isn't any reason to use it outside of twitter.
ΒScript has routines as first class citizens, and creates them in the lexical scope. In order to declare one, use the keyword "routine" followed by its name, parentheses containing a list of parameters, and a block body:
routine printStuff() {
print "stu" + "ff"
}
This means things like the block below are possible:
routine makeCounter() {
var a = 0
routine count() {
a = a + 1
return a
}
return count
}
var counter = makeCounter()
print counter()
print counter()
var counter2 = makeCounter()
print counter2()
with output:
1
2
1
Oh! And the return keyword works just like print: it expects the whole expression to be in the same line. if it isn't, the last part of the line needs to be an operator to indicate that it isn't over.
ΒScript allows the creation of classes with methods, initializers, and single inheritance. Classes are created using the 'class' keyword followed by the class name, and then a block. This block may contain method declarations, which are like routine declarations but don't require the keyword (the 'self' paramenter is implicit). In methods, it is possible to access the object's fields using 'self'. initializers are declared as methods with the same name as the class, and calling the class name will call the initializer, which returns a new instance of the class.
ΒScript only supports single inheritance, and it is done by declaring the class with the following sintax:
class (class name) < (superclass name) {(class declaration)}
superclass methods can be called using the "super" keyword. A class can only have one initializer, and they are not inherited.
The following code sample exemplifies pretty much everything explained here:
class A {
method() {
print "a method"
}
}
class B < A {
method() {
print "B method"
}
test() {
super . method()
}
}
class C < B {
C(callback) {
this . callback = callback
}
callCallback() {
this . callback()
}
}
function printStuff() {
print "stu" + "ff"
}
var thing = C(printStuff)
thing . test()
thing . method()
thing . callCallback()