Skip to main content

Less Lua Table Shift

Jake Russo

Jake Russo

Quarrel Designer

Just moments ago, I came upon a possible alternative route that I can take in the syntax. This route would lead to a schism in one area and a new unification in another. Perhaps the piece left alone is better that way. The two unified, well, that's a whole 'nother story...

It came to me while I was thinking about the table key'd values. I didn't really like how I had to mix positional semantics with named semantics. I think that it's fair to say, this is cognitively dissonant.

Then as I was imagining what other way we could do it, it dawned on me: the routine pattern => body syntax is sort of "adjacent" to hash lookup. It's also been something I've wanted to support; where you can actually allow a routine's pattern to be a lookup value. So, it almost seems like the perfect place to put the hash-table semantics.

So what kind of syntax? We have <...> available but I didn't want to use it for anything that is multi-line as I don't think it looks "correct" when you have a declaration separated over multiple lines.

this .= <
Agree? "looks odd to me" => yes
>

So after a few days of soul-searching, I think I've arrived at a nice compromise that adds an extra symbol (but many of Quarrel's operators are multi-symbol) and allows for a differentiation between what I am currently calling "data maps" and "routine maps".

Maps#

Maps are a new feature for Quarrel that allows you to implement a common paradigm in programming. Often called, associative arrays

# associative map
food .= <[
"grapes" => 12
"carrots" => 3
]>
# procedural map
eat .= <(
Text t? @ _ == "grapes", Number? n @ _ <= food\grapes[@] => (
food\grapes .= _ - n
)
Text t? @ _ == "carrots", Number? n @ _ <= food\carrots[@] => (
food\carrots .= _ - n
)
)>
Food ?= {
Text? name,
Number? quantity
}
# assertive map
Grocer .= <{
Food{f @ _ == "grapes", n} =>
n >> food\grapes @@ *("Not enough grapes")
food\grapes .= _ - n
Food{f @ _ == "carrots", n} =>
n >> food\carrots @@ *("Not enough carrots")
food\carrots .= _ - n
Food{f @ ~food[\][?](_), n} => *("No such food")
}>
Grapes ?= {Number? n} => Food("grapes", n)
Grapes(2) -> Grocer # food\grapes == 10

One of the hardest challenges with designing syntax (especially in this fashion of thoughtful aesthetic and mnemonic review), is not using a symbol in two separate places that would be in "contact" with each other. Keeping them separated (in usage) will help ensure that they are definitively different in the user's head. Meaning, when someone looks at a chunk of code, the use of a symbol doesn't require too much mental wrangling to separate what usage is displayed. For lack of better words: so that it "feels right".

Pattern Brackets#

Here, we have a rewrite of the Elixir queue example:

Empty ?= [] => true
Empty(+) __ => false
pop .= [h, t...] => [h, t]
push .= q, t => [q, t...]
front .= [h, ...] => h

Here we have the D version:

Node ?= <T>{
T? data
{Node<T> | Empty}? next
}
LinkedQueue ?= <T>{
{Node<T> | Empty}? head
{Node<T> | Empty}? tail
}
empty .= LinkedQueue? q => q\head == ()!
push .= LinkedQueue<T>? q, T? item => (
empty(q)
@@ q\head .= q\tail .= Node<T>(item)
~@ (q\tail\next .= Node<T>(item); q\tail .= q\tail\next)
)
push2 .= LinkedQueue<T>? q @ (_\head == ()!), T? item =>
q\head .= q\tail .= Node<T>(item);
push2(+) LinkedQueue<T>? q @ (_\head <> ()!), T? item =>
q\tail\next .= Node<T>(item); q\tail .= q\tail\next;
pop .= LinkedQueue<T>? q => T? (
empty(q) @@ *("Empty LinkedQueue")
item := q\head\data
q\head .= q\head\next
q\head == q\tail @@ q\tail .= ()!
item
)
enqueue .= push
dequeue .= pop
q .= LinkedQueue<Number>()
push q, 10
push q, 20
push q, 30
pop(q) #= 10
pop(q) #= 20
pop(q) #= 30
empty(q) #= true

Syndicate State Machine#

Wondering if the <{...}> circumfix operator could be used for defining actors:

BoxState ::= {value}
SetMessage ?= {newVal}
<{
box. BoxState(0)
SetMessage{n} =>
|-| <- "box: taking on new value ${n}"
box .= BoxState(n)
}>
<{
BoxState{v} :>
|-| <- "client: learned that box's value is now ${v}"
::SetMessage v + 1
}>
facetName .= <{Message{x}=>(facetName[!]!, <{...}>)}>
<{Message{x}=><{...}>}>
Mood ::= {user-name} # Assertion Contract
Stop ||= {msg} # Message Contract
<{ #[Actor]# }>
<{| #[Space]# |}>
# special pattern terms inside actor reactions
$ => init! # on start
^ => close! # on end
::Foo(bar) => ... # asserted `Foo` with argument `bar`
..Foo(bar) => ... # redacted `Foo` with argument `bar`
||Foo(bar) => ... # nessage `Foo` with argument `bar`
??Foo(bar) => ... # Captures observations of `Foo` with arguments `bar`
\|Foo(bar) => ... #

When condition's context is internal to an instance of a Baz contract which allows \argname access to the parametere names.

Baz ::= {qux}
::Baz(n) @ \qux == "wow!" => ... # asserted with when condition

Fields use the named argument/parameter syntax.

<{
foo. "value" # fields look like named arguments
}>
'ircd channel member'<ch>{ch, 'other connection'} -> <{
'current other name' .= ()!
}>
[...] # list
a[...] # list access
{...} # pattern
a{...} # named pattern "contract"
(...) # sequence
a(...) # named sequence "record"
<[...]> # nominal map (symbolic names mapped to data)
<{...}> # dialectical map (theoretical assertions mapped to sequences)
<(...)> # domain map (symbolic names mapped to dialectical maps)
<|...|> # categorical map (values mapped to number counts)
[|...|] # any list (at least one list element == true)
[&...&] # all list (all list elements == true)
{|...|} # any pattern (at least one pattern is fulfilled)
{&...&} # all patterns (all patterns are fulfilled)
Food ?= {Text? name, Number? quantity}
map .= {J} => {List(J)? js, {\J |-> ...]}? f => js -> j => f j
Functor ?= {A, B} => {A? _ => B? (...)}
Functor(Text, Number)? ({Text? t} => (t[@]))
Endofunctor ?= {A, B} => {A? _ => B? _} :: A == B
Endofunctor ?= {A} => {A? _ => A? _} # more simply
Monad ?= Endofunctor? _ ::
head .= {A} => {List(A)? l} => A? (...)
fst .= {A, B} => {(A, B)} => A? (...)
'==' .= Equalable? A ~> {A? _, A? _} => Truth? (...)
sayMe .= 1 => "One!"
sayMe[+] .= 2 => "Two!"
sayMe[+] .= 3 => "Three!"
sayMe[+] .= 4 => "Four!"
sayMe[+] .= 5 => "Five!"
sayMe[+] .= Number? x => "Not between 1 and 5"
factorial .= 0 => 1 #? Integer? n =>
factorial[+] .= Integer? n => n * (factorial n - 1)
{{A} => Equality{A}, Equality{A} |-> Truth? _}? '=='
type loc = real * real
fun square (x : real) = x * x
fun dist (x, y) (x', y') =
Math.sqrt (square (x' - x) + square (y' - y))
fun heron (a, b, c) = let
val x = dist a b
val y = dist b c
val z = dist a c
val s = (x + y + z) / 2.0
in
Math.sqrt (s * (s - x) * (s - y) * (s - z))
end
Location ?= Decimal? x, Decimal? y
square .= Decimal? x => x * x
dist .= Location{x, y}, Location{x', y'} =>
((square x' - x) + (square y' - y)) // 2
heron .= a, b, c =>
x .= dist a, b
y .= dist b, c
z .= dist a, c
s .= 50% * (x + y + z)