Skip to main content

Patterns

Patterns are a way to create complex guard routines that can be applied directly to expressions or more commonly they are used as parameter patterns for sequences (the two facets combined are known as routines).

Conceptually, they are used for creating interfaces, classes of objects, and for routine parameters. To do this, you can declare and thus name these patterns. This is known as a contract. There are two types of contracts: investigative and transformative. An investigative contract returns a truth value if the pattern is matched. A transformative contract will replace the value by applying some sequential transformation.

Transformative contracts use the ^ suffix and they are either applied like routines or placed in front of parameter names inside of parameter patterns. The contract will be invoked with the given value and they run this value through a sequence which theoretically will transform the value into what the contract author intends.

Investigative contracts use the ? suffix and they are applied in the same fashion as transformative contracts however they fail if the pattern doesn't match the given value.

Despite being mostly a symbolic language, named/categorical values are both numerous and, for lack of a term, "exist in the realm of the named" meaning that they should have names because they are named things and not syntax. Therefore, Quarrel provides some named contracts and builtins. Although not being a true tenet, Quarrel does intend to be capable of modeling programs without excessive boilerplate to create a platform that is usable.

Number? 10
Integer? 100
Decimal? 10.5
Binary? 2^^10110
Octal? 8^^12312
Hexadecimal? 16^^1A2F31
Text? "Wow"
Symbol? "c" # also written with double quotes but restricted to one grapheme
Date? `2020.10.04` # 8601 format with `.` instead of `-` (also supports week)
DateTime? `2020.10.04 15:42:58+00:00` # also supports shortened time offsets
Sequence? 1, 2
Table? [3, 4, b. "foo"]
Pattern? {Number? n}
Aggegrate? [2, 3] # any sequence, list or pattern
Truth? 10 > 5 # any yes or no result
Yes? test 10 # also True? is supported
No? fail 5 # also False? is supported
Container? x # any non-literal
Datum? d # any non-pattern, non-routine, non-aggregate
Data? d # any non-pattern, non-routine
Actor? a # any non-null element
Empty? e # a null element
Reference? r # is this value a reference to another datum

Patterns not only provide a means to understand incoming data, they also indicate the structure of your data and have the ability to generate data according to the specification. When you pass an expression through a pattern, you get a context where the names provided in the pattern are patched to the things you provided when you invoked the pattern.

Pair ?= first, last
Pair? [1, 2] -> \first + \last #= 3

The base expression type of the pattern becomes the aggregate that is formed when the data is

How to...#

create a pattern#

pat ?= {x, y} # investigative
pat ?= x, y # shorthand
pat ^= Integer? x
# this will fail since an investigative contract needs to be a functional routine
pat ^= Integer? x => Text(x)
foo .= pat^ f => f & f

Patterns are first-class and can be passed around and defined anonymously

create a contract#

bigNum ?= n >> 10 # pattern expression
bigNum ^= n ** 10 # sequence expression

Investigative contracts are just patterns that return truth values when given arguments. Transformative sequences can be as minimal as a simple expression (like above) or full-fledged routines.

apply a pattern#

Patterns are applied within parameter patterns or structural patterns. You then apply a parameter pattern by...

  • ...combining it with a sequence via the => operator to become a functional routine.
  • ...combining it with a table via the -> operator to become an iterative routine.
  • ...supplying it to a structural sequence of elements to become a record.
n.=10;
foo .= ({Integer? x}=>(x * x))(n) #= 100
result .= [1,2,3] -> {Integer? _} #= [true, true, true]
x .= "2"
bar .= Integer^ x => x + bar x #= 6
insideOfExpression .= 10 + Integer(x) + 4 #= 16

add an additional investigative contract pattern#

Pair1 ?= {first, second}
Pair1 ?= {first, second}

Pattern Sub-Language#

Inside of patterns, many declarative operators describe the same features they would outside of patterns. There are some operators specific to this sub-language.

&

This operator creates a multi-faceted pattern where two connected patterns will now be subsequently tested against some datum and only approves the datum as fulfilling those patterns if both patterns are valid for the datum.

DiffPatternPair ?= <X, Y> X? a, Y? b
FirstTextPair ?= Text? a, b
{DiffPatternPair{} & FirstTextPair{}}? ("foo", 2)

|#

This operator creates a multi-faceted pattern where two connected patterns will be tested against some datum and unlike &, if only one pattern matches the data, it's a pass.

{Number{} | Text{}}? n => ...

->#

This operator takes a datum (with or without a preceding contractual obligation) and (when the contract passes) applies an additional check on the datum via a sequence. Keep in mind that Quarrel allows patterns to directly compare literal values in addition to just other patterns but when you need to do some calculations or check some internal parameter, then you reach for @. You can use the _ operator to indicate a single parameter routine which improves readability:

PositiveNumber ?= {Number? n -> _ > 0}

<-#

This operator applies the sequence to the argument first and then performs the contractual check. Here, the return value is the new parameter, replacing the argument provided.

EagerPos ?= {
PositiveNumber? n <- <(
Number? n => n
Text? t => +t
)>
}

...#

When fixed to a container name, this operator creates a list of values that encompasses a slice of the arguments provided. It allows parameter sequences to be variable length. When given no container name, it implies that this slice of values is not needed.

first .= x, xs... => x
last .= xs..., xn => xn
second .= x, y, ... => y
edges .= x, ..., xn => [x, xn]

_#

Signifies an element of the pattern that is discarded (though you can still test it against contracts). Interestingly, the value is not truly discarded but merely left unnamed. Inside of sequences, you can access a default parameter list; a single parameter is concidentally accessed with the same operator, _. However, when multiple parameters are involved, you must use \1 , \2, and so on.

fooBar .= Number? _ -> _ > 5 => _ / 5
barBaz .= Number? _, Number? _ => \1 + \2

?#

The ? operator takes some pattern or contract and requires it to pass ("obligates" it) to some container or value. This is the main way of using contracts and also can be used with pattern literals.

div .= Integer? x, Integer? y => x / y
div 10, 5 #= 2
div 10, "5" #= Error is thrown

#

^#

Operators#

Binary and unary operations that act specifically on Patterns.

->#

This operator will do several difference types of transformations depending on the receiver. In the case of being received by a sequence, the supplying sequence is provided as arguments to the receiving sequence. In the case of being received by a pattern, the supplying sequence is tested against the pattern to determine if it matches.

{Sequence? x, Sequence? y} => Any? y(...x)
{Sequence? x, Pattern? y} => Truth? (y? x)
{Sequence? x, Contract? y} => Truth? (y? x)

Examples#

seq .= (1, 2, 3)
seq -> |-| #= 3

The |-| builtin container is for input and output and in this case, it prints whatever value is provided to it to the STDOUT.

<-#

Take the receiving values as arguments to an invocation of the sequence.

{Sequence? x, Sequence? y} => Any? x(...y)
{Routine? x, Sequence? y} => Any? x(...y)
{Sequence? x, Any? y} => Any? x(y)
{Routine? x, Any? y} => Any? x(y)

Examples#

g .= (\1 + \2 + \3)
g <- (1, 2, 3) #= 6

+#

Invokes any sequence provided as an argument to the + operator.

{Sequence? x, Any? y} => Any? x! + y
{Any? x, Sequence? y} => Any? x + y!

Examples#

(1,2,3) + 5 #= 8

==#

Invokes any sequence provided as an argument to the == operator.

{Sequence? x, Any? y} => Any? x! == y
{Any? x, Sequence? y} => Any? x == y!

Examples#

(1 + 1) == 11 #= false
(1 & 1) == 11 #= true

<>#

Invokes any sequence provided as an argument to the <> operator.

{Sequence? x, Any? y} => Any? x! <> y
{Any? x, Sequence? y} => Any? x <> y!

Examples#

(1 + 1) <> 11 #= false

>>#

Invokes any sequence provided as an argument to the >> operator.

{Sequence? x, Any? y} => Any? x! >> y
{Any? x, Sequence? y} => Any? x >> y!

Examples#

(1 + 1) >> 11 #= false

<<#

Invokes any sequence provided as an argument to the << operator.

{Sequence? x, Any? y} => Any? x! << y
{Any? x, Sequence? y} => Any? x << y!

Examples#

(1 + 1) << 11 #= false

>=#

Invokes any sequence provided as an argument to the >= operator.

{Sequence? x, Any? y} => Any? x! >= y
{Any? x, Sequence? y} => Any? x >= y!

Examples#

(1 + 1) >= 11 #= false

<=#

Invokes any sequence provided as an argument to the <= operator.

{Sequence? x, Any? y} => Any? x! <= y
{Any? x, Sequence? y} => Any? x <= y!

Examples#

(1 + 1) <= 11 #= false

***#

Invoke the sequence as many times as indicated by the other operand.

{Sequence{\1}? x, 1} => \1? x!
{1, Sequence{\1}? y} => \1? x!
{Sequence{\1}? x, Integer? n @ _ >> 1} => List{\1}? [x! #[1]#, x! #[2]#, ..., x! #[n]#]
{Integer? n @ _ >> 1, Sequence{\1}? y} => List{\1}? [y! #[1]#, y! #[2]#, ..., y! #[n]#]

Examples#

n .= 5; (n .= n + 5) *** 3 #= [10, 15, 20]

Method Operators#

Method operations that are performed on Sequence containers.

[+]#

Add a new routine to the container

{Container{Sequence}? self, Sequence? x} => self

Examples#

convert .= {Number? n, Truth? percent} => (percent @@ n ~@ n * 100)
convert[+] .= {Number? n} => n

[:]#

Clone the container of sequences

{Container{Sequence}? self} => (new := self)

Examples#

fs .= {1} => "one"
fs[+] .= {2} => "two"
apply .= {Sequence? f, Number? arg, Number? times} => (
count := times
i := arg
[(i .= i + 1, count .= count - 1) >> 0 -> (
f[?] i ~@ f[+] .= {\i} => "{i}"
f[i]
)]
)
apply(fs[:], 0, 4) #= ["0", "one", "two", 3"]
fs[_:] #= [{1}, {2}]

[_:]#

Cloned list of all parameter patterns

Examples#

[?]#

Check for a matching parameter pattern

{Container{Sequence}? self, Pattern? x} => Truth? result

Examples#

[-]#

Change a routine by looking up it's parameter pattern

{Container{Sequence}? self, Sequence? x} => self
{Container{Sequence}? self, Routine? x} => self

Examples#

[@]#

Count of how many routines are contained

{Container{Sequence}? self} => Number? n

Examples#

Contract Graph#

%%{init: {'theme':'dark'}}%% graph LR; Contract --> Pattern; Pattern --> Aggregate; Aggregate --> Data; Data --> ac([Actor]); Pattern --> an([Any]);