Soundness is a loose collection of open-source libraries designed to take full advantage of new features of Scala 3 to write web and command-line applications, with a focus on lifting more operations to the type-level with safe and expressive syntax.
Soundness includes libraries for working with HTML, CSS, JSON, XML, CSV, typesafe strings, ANSI-escaped strings and Markdown, both calling HTTP and serving HTTP or the command line. Additionally, support for disk I/O, filewatching and the standard UNIX directory layout, environment variables, shell processes (including native keyboard interactivity), cryptographic functions, tabular output and regular expressions is provided, and includes representations of colors, directed acyclic graphs, multilingual strings, MIME types and generalized paths. Fundamental to these projects are utilities for generic derivation, checked interpolated strings, annotations, streaming operations, type providers library decoupling and unit testing with a WebDriver API for browser testing. A module also exists for Scala syntax highlighting. Everything builds upon minimal set of common tools.
Soundness embraces two core principles:
- Impossible states should be unrepresentable
- Transitions between states should be total
Together, these two principles eliminate an entire class of bugs.
More specifically, each library under the Soundness umbrella adheres to the following principles:
- typesafe—taking full advantage of the Scala 3 typesystem
- checked exceptions—but only if you choose to use them
- safe literals—interpolated strings, checked at compiletime with Contextual
- forbidden
null
—no method should ever returnnull
, and no value should ever benull
, guaranteed by the Scala typesystem - typeclasses—extensibility provided through Wisteria's generic derivation
- immutability—mutation of state is avoided, even when working with streams
- decoupled—modules use Anticipation to minimise unnecessary dependencies
- functional programming—embracing the fundamentals of FP, but avoiding the complexity
- small APIs—above all, code should be legible, natural and elegant
Scala 3's typesystem offers a rich variety of types for representing and combining constraints on values. This presents an opportunity to encode a value's invariants—facts about the value which we know will always be true—precisely in its type. These invariants then give us certainty that operations involving the value are safe. Or, that they're simply impossible. In general, this reduces the amount of branching, including exception handling, that's required in code. Extensive use of immutable datatypes adds further guarantees.
In particular, the type Text
, an opaque type alias of String
, is used in place of String
at
every opportunity. Text
has a variety of useful methods, all of which are total functions or
declare their exceptions. A String
may always be converted to a Text
with the show
extension
method, and a Text
converted to a String
by calling its method s
.
The latest release of Scala 3 introduces opt-in exception checking, and every Soundness method declares the exceptions it may throw in its signature. This makes it easy to write prototype code with a "let it fail" attitude and exception-checking off, and to migrate to production-quality code just by turning exception-checking on, and having the compiler require handlers for each exception—but without needing to transform types or switch to a monadic coding style.
Effectively, this transforms every partial function into a total function; when combined with the wise philosophy of making impossible states unrepresentable, exceptions become even more exceptional.
The introduction of null
into ALGOL was described by Tony Hoare as his "billion-dollar mistake",
though it has persisted in numerous programming languages since. Scala 3 introduces new checks to
help avoid null
references, and Soundness projects take full advantage of these.
When constructing a value, such as a JSON object or a URL, from a string literal, all the
information is available at compiletime to check the validity of the string's contents. So,
whenever possible, this is provided with interpolated strings, such as url"https://github.com/"
,
using Contextual. Since checks are performed at
compiletime, there is no risk of runtime exceptions arising from these values.
The typeclass pattern, provided through contextual values (given
s) is used extensively by
Soundness libraries to provide ad-hoc polymorphism (in preference to subtype polymorphism). This not
only allows user-defined types to participate naturally in all kinds of Soundness APIs, but also
facilitates interaction between Soundness libraries and third-party libraries.
When working with libraries in different domains, it is common to need integration between them. For example, an HTTP server should be able to serve a XML value with the correct MIME type, without too much boilerplate. That is easily achieved by making one library a dependency of the other. But the user of a XML library should not need to include an HTTP server (nor should an HTTP server require a XML library). The solution is to make use of minimal typeclasses provided by Anticipation to maximally decouple independent libraries.
Every API introduced by a Soundness library should fit on one side of a business card. It should never
be difficult to learn, and composition of APIs should be preferred over specialized solutions. Names
should be meaningful and appropriately unique: that is to say, sharing a name with an existing
concept or entity if they represent that entity, but introducing new nomenclature if they represent
something new. Short names are preferred, but arbitrarily-abbreviated names are not. Objects are
primarily composed through selection (the .
operator) and application, rather than monadic mapping
and flat-mapping, since exceptional cases may be handled with checking. The amount of code in each
library should also be small.
- extensive use of the immutable
IArray[Byte]
type for random-access byte-data - streaming provided using
LazyList
s - use of the
T | Unset
union type for optional parameters, without the need to wrapT
inSome
or usenull
Soundness projects already use Scala 3's enhanced exception checking, and in the future, streaming APIs
built on LazyList
s will be enhanced to use the experimental capture-checking functionality that is
expected to be introduced in Scala 3 soon to provide better safety.