# Solace A language to find solace after using JavaScript Proposal: A strongly typed, programming language that compiles to JavaScript, with the following features: * Error unions: `Error!string` * Optional values: `?string` * scoped blocks * exhaustive pattern matching * "unreachable" keyword * built-in reactivity system (using signals?) Syntax example (very early proposal, might change): ```ts //imports work just as in ES6 import { func1, func2, func3 as funFunc } from './MyCoolModule' import Cool from './MyCoolModule' // Declarations can be constant, variable or live (a.k.a. reactive): // Constants can never change const c = 299792468 // neither type nor value will ever change c++ // Error! Constant values cannot change. const car = struct{ tires: 4, engine: true } car.tires = 5 // Error! Unlike in JS, struct values cannot be changed, either // Variables can be changed, as long as their type and shape stay the same: var vehicle = struct{ tires: 2 engine: true } vehicle.engine = false // no problem vehicle = struct{ // also fine tires: 2 engine: false } vehicle = struct{ // Error! Type cannot be changed color: 'red' } /* Types and Shapes? * * Solace differentiates between types (as in string, number, boolean) * and shapes. While most types don't have shapes, structs ("objects") have. * Solace is strict about shapes of structs and doesn't allow any undefined * properties. Instead, property names should be known from the beginning * and can be explicitely optional and set to null, if the value isn't known */ // TODO: introduce a dict type, that can be used without shape constraints? // Live values can be changed and their changes are tracked live speed = 50 track speed { console.log(`Driving ${speed} km/h now`) } speed++ // logs speed automatically speed = 'fast' // gives an error, because the type cannot change // Computed values are basically getters (similar to Vuejs' computed) computed apparentSpeed { // pattern matching is another useful language feature that is explained in // further detail later return match speed { 10 -> 'walking speed' _ < 50 -> 'inside town, maybe' _ -> 'outside town, hopefully' } } // Values can be optional var userName: ?string = None // we don't know the name, yet // Errors can be involved as well var userName: !string = Error('user not found') if (userFound) { userName = 'Hans' } /* Optional values and Error Unions explained: * * ?string would be string | null in Typescript, * !string would be string | Error, * !?string would be (string | null) | Error * Error unions can be specific: NetworkError!string * Optional values can either be a value or None (there is no null in Solace) */ // undefined var userName: string // undefined, because it is not initialized userName = undefined // Error! You cannot set anything to undefined // In Solace, a value can either be explicitely optional and set to None, // or it can be undefined, because there was no assignment, yet. // This makes the distinction between undefined and null very clear: if (userName is undefined) // not initialized at all if (userName is None) // explicitely set to None // Guards: certain places can make use of guards to tighten their matches: match userName when Some(name) { name => console.log('User is named', name) } else { console.log('User has no name?!') } // defer allows you to run a code block after a function finished async fn deleteUser(userId: string) { const user: ?User = await fetch(`/api/users/${userId}`) // lets not forget to send the user a mail defer when user is Some(_) { sendEmail(userId, `Greetings ${user.name}, you have been deleted!`) } await deleteUser(userId) } // functions look like this fn doMath(): ?number { if (!computer.ready) return None return 42 } // ```