Repository navigation
Extensions
These are some possible extensions to the language.
The mutation directive allows block names to be redefined and collections to be mutated.
kesh 2021 + mutationNames in lexical scope may be marked as variable with let and redefined with set.
let mutated: false
set mutated: trueObject fields may also be marked as variable, and redefined using dot notation.
joe: [ let name: "Joe", age: 27 ]
set joe.name: "Joseph"The entire object may be marked as mutable with the * unary operator.
joe: *[ name: "Joe", age: 27 ]
set joe.age: 28
set joe.nickname: "Joey"Arrays and other indexed collections may also be marked as mutable.
people: *array[]
people.push joeKeyed collections are always mutable.
Multiple object fields can be redefined in one go by unpacking to the object.
set joe.(name, age): ('Joey', 28)The above line achieves the same as:
(name, age): ('Joey', 28)
set joe.name: name
set joe.age: ageUnpacking also works with objects and arrays.
set joe.[name, age]: [ name: 'Joey', age: 28 ]
set joe.(name, age): ['Joey', 28]It's also possible to unpack all of another object's enumerable fields with the spread/rest operator ....
set joe.[...]: [ name: 'Joey', age: 28 ]This is equivalent to ECMAScript's Object.assign.
Within a set's expression, the placeholder operator _ refers to the name's existing value, enabling augmented assignment.
let count: 0
set count: _ + 1
--> #number 1Existential assignment is also useful.
set options.duration: _ ? 100 -- provide fallback by setting to 100 only if #noneOne can also use helper functions increment and decrement on mutable #numbers.
increment count
--> #number 2decrement count
--> #number 1The fp directive enables compose and pipe operators, and imports all functions from rambda into the module scope.
kesh 2021 + fpThe forward >> and backward << composition operators compose unary functions.
square >> negate >> print -- forward composition
print << negate << square -- backward compositionThe pipe |> operator allow values to be piped into expressions.
The ^ placeholder is used to place the piped value inside the right-hand expression.
Pipes are equivalent to the TC39 proposal for smart pipelines.
answer: [1, 2, 3, 4]
|> sum -- unary function application
|> ^ * 4 + 2 -- expression
--> #number 42Piping into a composed function:
answer |> square >> negate
--> #number -6.48074069840786The placeholder directive enables extra functionality for the _ placeholder.
When unpacking, it can be used to ignore a value.
kesh 2021 + placeholder
(_, answer): (true, 42)The globals directive imports all fields of the global object into the module scope.
kesh 2021 + globals + mutation
set global.answer: 42
answer
--> #number 42
Math.sqrt answer
--> #number 6.48074069840786The return directive enables the return statement for explicit or early termination of functions.
kesh 2021 + return
sqrt: (number) ->
if number < 0 then return
-- todo: calculate the square root of numberThe async directive enables the async and await keywords.
kesh 2021 + async + globals
sleep: async (seconds) ->
Promise (resolve) -> set-timeout(resolve, seconds * 1000ms)
print "Hold up"
await sleep 1s
print "Hey!"The match directive enables pattern matching using the match keyword.
kesh 2021 + match
stage: match person.age
0 -> 'infant'
1 | 2 -> 'toddler'
3..17 -> 'kid'
18..59 -> 'adult'
60..123 -> 'senior'
124.. -> 'kurzweil'
#any -> 'invalid'The loop directive enables imperative loop statements with support for break and continue statements.
kesh 2021 + loop + async + mutation
-- infinite loop
loop
await client.listen()
-- repeat a block x times
loop 3
print "hey"
-- loop over an iterable's elements
loop 1..10 as number
if number mod 3 = 0 then continue
print number
-- loop over a collection's own enumerable fields
loop score as (player, points)
print("{ player.name }: { points } points")
-- traditional while loop
loop while everything = 'ok'
print "It's all good"
-- untraditional until loop
loop until done
-- todo: process something
if nothing-to-do then set done: true
-- traditional for loop
loop for { let i: 0, i < 9, increment i }
print iloop iterates an iterable or a #number by default.
loop while and loop until loops on the basis of a predicate.
loop for is equivalent to ECMAScript's for loop, except using a block to specify iteration parameters.
Loops are statements, not expressions, and don't return a value. However, they can be named, making it possible to break or continue a specific loop by name. Yes, this happens to be identical to ECMAScript's label statement.
outer: loop {
print "Entered the outer loop"
inner: loop {
print "Entered the inner loop"
-- This would break only the inner loop
--break
-- This breaks the outer loop
break outer
print "This point will never be reached"
}
print "Exited the outer loop"
}
--> #text "Entered the outer loop"
--> #text "Entered the inner loop"
--> #text "Exited the outer loop"The generator directive enables generator functions, which are regular function producing values with the yield keyword.
kesh 2021 + generator + loop + mutation
id-maker: gen () ->
let index: 0
loop
yield index
increment index
generator: id-maker()
generator.next().value --> 0
generator.next().value --> 1
generator.next().value --> 2The yield-from keyword delegates to another generator or iterable, equivalent to ECMAScript's yield*.
func-1: () \-> yield 42
func-2: () \-> yield-from func-1()
generator: func-2()
generator.next().value --> 42The with directive enables with statements for generators, similar to Python's with statement.
This is useful in a number of scenarios. A common one is to ensure that a resource is released after use.
kesh 2021 + with
open: async gen (path) \-> -- async non-returning generator function
file: await fs.open(path)
print "file opened"
yield file
file.close()
print "file closed"
with await open("./hello.txt") as file
print file.contents
--> #text "file opened"
--> #text "Hello, world!"
--> #text "file closed"The regex directive enables regular expressions as an embedded language.
kesh 2021 + regexRegular expressions are written as XRegExp expressions within code or text literals tagged with regex.
Code literals compile down to native RegExp objects.
re`(?im)^[a-z]+$` -- leading mode modifierre('gi')`\b(?<word>[a-z]+)\s+\k<word>\b` -- flags as argumentText literals are evaluated at runtime, which require the XRegExp module to be automatically imported.
hours: re'1[0-2]|0?[1-9]'
minutes: re'(?<minutes>[0-5][0-9])'
time: re"\\b { hours } : { minutes } \\b" -- interpolationThe exception directive enables exceptions and try…avert statements.
The fail keyword is used to "throw" or "raise" an exception.
The avert keyword is used to "catch" an exception thrown/raised within a try block.
kesh 2021 + exception
check: (roentgen) ->
if roentgen > 3 then fail Error "Dangerously high radiation levels"
try { check 3.6 } avert (cause)
console.info Error("Well, that's not great, but it's not horrifying", [cause])As the keywords imply, an exception will cause the program to fail unless averted.
As the example implies, a fail should not be handled carelessly.
The contract directive enables Eiffel like require and ensure statements within functions.
Each of the statements evaluate one (inline) or more (block) conditions (expressions) that must be fulfilled.
For the ensure statement, result refers to the return value of the function.
kesh 2021 + contract + fp
sqrt-int: (x) ->
require x >= 0
ensure
result * result <= x
(result + 1) * (result + 1) > x
x |> sqrt >> floorThe defer directive enables the defer statement, similar to that of Go.
kesh 2021 + defer + loop
count: () ->
print 'counting'
loop 1..3 as number
defer print number
print 'done'
count()
--> #text "counting"
--> #text "done"
--> #number 3
--> #number 2
--> #number 1The order directive enables explicit order of operations for arithmetic operators, also known as PEMDAS or BODMAS.
It activates a source code formatter that enforces order of operations by inserting parens, respecting any existing groups.
This improves readability by removing ambiguity when order of operations is expected.
kesh 2021 + order
-- before save:
print 1 + 2 * 3 - 4 / 5
print (1 + 2) * 3 - 4 / 5
-- after save:
print 1 + (2 * 3) - (4 / 5) --> 6.2
print (1 + 2) * 3 - (4 / 5) --> 8.2There's no implicit order of operations.
Without order enabled, mathematical expressions are evaluated left-to-right, with parens used for grouping.
print 1 + 2 * 3 - 4 / 5 --> 1
print 1 + 2 * 3 - (4 / 5) --> 8.2Directives may also be grouped into a named profile. The following are provided by the compiler.
The standard profile enables all the standard extensions for the active version of the language.
kesh 2021 standardSome candidates for inclusion:
The web profile imports window from the global object.
kesh 2021 web
alert "Hello, web!"To open the print dialog, window.print() must be used, as print is an alias for console.log in module scope.
The strict profile enforces certain restrictions.
kesh 2021 strictThe archaic profile enables obsolete keywords that went out of fashion a long time ago.
kesh 2021 archaic
const answer: 42
function query(question): {
if (~question) {
return answer
} else {
throw new Error('No question, no answer')
}
}Please don't.
The define directive enables definition of language features such as keywords and operators.
It could be done something like this (nothing here is written in stone):
kesh 2021 + define
define statement 'return': (exp) -> …
define postfix-operator '?': (lhs) -> …
define binary-operator '?': (lhs, rhs) -> …
define statement 'loop': (exp) -> …This programming language only exists as a design. I'm unlikely to ever get the chance to write a compiler for it.
Feel free to steal any parts of it that you like. Ideas are better stolen than forgotten. (They're not my ideas anyway.)