We have significantly expanded the Standard Library with this release. In particular, System.String has undergone significant expansion.
System.String Highlights
between
string quotedWords = '"duck" "swan" "crab"'; // 'between' is smart enough to allow the same string to be used as a start and end delimiter string[] words = quotedWords.between('"', '"'); Console.log(words); // [ "duck", "swan", "crab" ]
Documentation page: click here
format: C-like printf
"%s is %d years old".format("Joe", 10) // Joe is 10 years old
Documentation page: click here
escape
"a\r\nb".escape() // a\\r\\nb
Documentation page: click here
truncate
string text = "The quick brown fox jumped over the lazy dog."; Console.log(text.truncate(9)); // "The quick..."
Documentation page: click here
repeat
"*".repeat(5) // *****
Documentation page: click here
count
"foobar".count("foo") // 1 "FOOBAR".icount("foo") // 1
Documentation (count): click here
Documentation (icount): click here
contains
"abc".contains("b") // true "ABC".icontains("b") // true
Documentation (contains): click here
Documentation (icontains): click here
New System.String Methods
Here are all the new methods available for strings in JS++:
- between – Gets substrings between two delimiters (does not use regex)
- compact – Removes whitespace globally
- contains/icontains
- count/icount
- countLines
- countNonEmptyLines
- startsWith/endsWith
- escape/unescape – Escape the escape sequence characters (e.g. \n -> \\n)
- escapeQuotes/unescapeQuotes
- format – Similar to C’s printf
- insert/append/prepend
- isEmpty – uses .length === 0 rather than str1 === “” for performance, not everyone has time to benchmark every detail
- isLowerCase
- isUpperCase
- isWhitespace
- joinLines – collapses a string composed of multiple lines into a single line
- joinNonEmptyLines
- padLeft/padRight – remember the NPM debacle?
- quote/quoteSingle – wraps the string in quotes
- unquote – removes quote pairs
- repeat – “*”.repeat(3) == “***”
- reverse
- splitLines – splits a string into a string[] (array) based on newlines
- trim, trimLeft, trimRight, trimMulti, trimMultiLeft, trimMultiRight
- truncate – Cuts off the string at the specified length (with support for custom ellipsis)
There are close to 50 new string methods (48 including overloads, 39 otherwise), and these methods should cover most application-level usages. With documentation, this resulted in +1400 new lines of code to System.String. I’m happy to announce we actually still have more methods (for System.String and others) on the way.
Every single method is documented. All documentation is online and available at the System.String index page.
We avoided regular expressions as much as possible to avoid runtime FSM construction, which takes time and space. Therefore, prefer JS++ methods such as "abc".endsWith("c")
over the traditional regex/JavaScript /c$/.test("abc")
.
The best thing about JS++ is that it’s a compiled language. This gives you performance benefits that a JavaScript library with string utilities can never give you. For example:
if ("abc".isEmpty());
becomes:
if ("abc".length===0);
and
"abc".quote()
becomes:
'"'+"abc"+'"'
The astute observer will notice that both the above methods can be further optimized to reach “perfect” optimization. However, there is no optimizing compiler inside JS++ yet, and inserting branching logic into the code generator will result in technical debt.
Our goal with the Standard Library is to make it easier than ever to write applications compared to JavaScript. Side effects of our work on the JS++ Standard Library are performance, size, and correctness. JS++ dead code elimination means we can add hundreds of methods to System.String, but you only pay for the methods you actually use. For performance, not every team can afford to hire a JavaScript performance expert. Even if you have the performance expert, he can’t be expected to micro-optimize and benchmark every method.
Finally, with the JS++ Standard Library, we can fully avoid the NPM left-pad debacle.
import System; Console.log("1".padLeft(4, "0")); // "0001"
fromString
Previously, to convert a string to number in JS++, it was a little unintuitive. For example:
int x = +"1000"; // use the unary + operator
For all numeric types, we’ve introduced the fromString
, fromStringOr
, and fromStringOrThrow
static methods. The above example can be re-written to use Integer32.fromString:
int x = Integer32.fromString("1000");
Advanced Generics
JS++ 0.8.4 introduces covariant and contravariant generic types (including upcasting and downcasting for types with variants). Covariance and contravariance are based on use-site variance. At this time, we are not introducing declaration-site variance at all; we have higher priorities. In addition, we’ve introduced generic constraints (subtype constraints, multiple constraints, wildcard constraints, and more).
Finally, we have support for generic functions and generic static methods.
Everything from basic to advanced generic programming in JS++ is covered in our generic programming documentation.
When we released version 0.8.0, we introduced only basic generics. In today’s 0.8.4 release, you can consider generics fully implemented.
I highly encourage reading the generic programming documentation. To put it all together, here’s generic covariance and contravariance together with use-site variance:
import System; abstract class Animal {} class Tiger : Animal {} abstract class Pet : Animal {} class Dog : Pet {} class Cat : Pet {} class PetCollection { Pet[] data = []; void insert(descend Pet[] pets) { foreach(Pet pet in pets) { this.data.push(pet); } } ascend Pet[] get() { return this.data; } } auto myPets = new PetCollection(); // Write operations (descend, covariance) myPets.insert([ new Dog, new Cat ]); // myPets.insert([ new Tiger ]); // not allowed // Read operations (ascend, contravariance) Pet[] getPets = []; Animal[] getAnimals = []; ascend Pet[] tmp = myPets.get(); // read here foreach(Pet pet in tmp) { // but we still need to put them back into our "result" arrays getPets.push(pet); getAnimals.push(pet); } // Now we can modify the arrays we read into above getPets.push(new Dog); getAnimals.push(new Dog); getAnimals.push(new Tiger); // getPets.push(new Tiger); // ERROR
Other Changes
- Fix return types for System.String.charAt and System.String.charCodeAt
- Fix type promotion to ‘double’. We now handle this better than languages like Java and C#. Thanks to our lead engineer, Anton, for the idea.
- isEven() and isOdd(). You might think this is fizz buzz, but if you’re using the modulus operator, it’ll be slower. We use bitwise operations, and you might be interested in reading this article on how we took advantage of overflow behavior to improve performance while preserving correctness.
- Fix System.Array.map and System.Array.reduce to support wildcard generic types.
- Type inference of generic parameters for function calls. This is needed for System.Array.map and System.Array.reduce, but it’s also available for user-side code.
- Fix
System.Console.error
when no console is available - Fixed error message with incorrect type for setters defined with no accompanying getters.
- Fixed
private
access modifier for modules in a multi-file setting. - Fix callback types as generic arguments
- Fix enum bitwise operations to reduce explicit casting