Types

Types do not have to be declared in Quirl. The interpreter infers a value's type from the value itself. The interpreter also converts between compatible types automatically as necessary. The language is designed so that these conversions do not cause ambiguities. It is nevertheless good to know about types.

Quick links: AppBeyondBoolCircCompConstFloatFunIntLangListModnModpPolQuadRatSetSqrumStructTextTupleTurnTypeWildcard

Numbers

Quirl's focus is on numeric calculations. Hence, it supports multiple numeric types.

The following table lists which of the numeric types and the polynomial type Pol get automatically converted into each other. The conversion is from the type in the table row to the column type. So other numeric types can be converted to Float, but Float can not be converted to any other type. This is because Float does not guarantee precision while the other types do.

Int Rat Pol Quad Sqrum Float
Int yes yes yes yes yes
Rat ? yes yes yes yes
Pol ? ? ? ? ?
Quad ? ? ? yes yes
Sqrum ? ? ? ? yes
Float no no no no no

A question mark means that convertibility depends on the specific value. For instance, the rational number 1 can be converted to an integer 1, but the rational number 2/3 can not be converted to an integer. Conversion is always possible when the same notation for a number exists in different types.

Int

The Int type is for arbitrarily large integers. They are written as a sequence of decimal digits, optionally with a leading plus or minus sign.

# Examples
 0
 2658455991569831744654692615953842176
-1
+00056 # same as 56

Int implements the traits Operand, Ordered, Ring – and hence also Soup, Group and Sing. The meanings of the related core functions are straightforward:

function meaning
add classical addition
neg classical negative
mul classical multiplication
ord smaller than or equal to

Besides, Int is closed under the modulo operation. It is available as mod core function and returns the remainder when of the Euclidean division of its first argument by the second argument. The result is always non-negative.

Rat

The Rat type is for rational numbers. Rational numbers are all numbers that can be expressed as the ratio of two integers. This includes all integers of course, as any integer m can also be expressed as m to 1. You can input rational numbers as a vulgar fraction of an integer and an unsigned integer using the solidus / as fraction slash in between.

# Examples
  3/4
-22/7
 21/7 # same as 3

But you can also write them as decimal fraction with a dot . as decimal separator. Prepend the repetend of a repeating decimal with an apostrophe '.

-0.125     # same as  -1/8
 0.'142857 # same as   1/7
 1.108'3   # same as 133/120

Rat implements the Operand, Ordered, Rootable, Scalable, Field (and hence Soup, Group, Sing, Ring) traits.

function meaning
add classical addition
inv reciprocal
neg classical negative
mul classical multiplication
ord smaller than or equal to
root an n-th root
scale classical multiplication

Quirl automatically reduces rationals to their lowest terms. The den core function returns the resulting denominator.

Quad

The Quad type implements all numbers that are solutions of a quadratic equation with rational coefficients. These solutions can be rational, but they can also be irrational or even complex numbers with an imaginary part. A number's rational part always comes first, if it exists. The irrational or imaginary part may consist of an optional rational factor followed by the square root of an unsigned integer and/or followed by the imaginary unit i. A square root is symbolized by a backslash \.

# Examples
\2        # square root of 2
3/7i      # 3/7 on the imaginary axis
3\7i      # 3 times the square root of 7 on the imaginary axis
1/2+1/2\5 # the golden ratio
-1-i

Quad is a branded type with neutral brand. Neutrally branded Quads are rational. Each other brand, combined with the neutral brand, represents a quadratic field. A number's brand is determined by the square-free integer whose square-root occurs in the irrational or imaginary part. The imaginary unit i contributes a factor of -1 to the brand. For instance, the golden ratio's brand is 5. The brand of 3\7i is -7.

Quad implements the Operand, Rootable, Scalable, Complex (and hence Soup, Group, Sing, Ring, Field) traits.

function meaning
add classical addition
arg complex argument
con complex conjugate
inv reciprocal
neg classical negative
mul classical multiplication
root an n-th root
scale multiplication with a rational

Sqrum

As a branded type, Quad can only add, subtract, multiply or divide numbers of the same or neutral brand. The Sqrum type does not have this limitation. Sqrum is a contraction of square-root sum. It can store any number that is the sum of solutions of quadratic equations with rational coefficients. This happens to include the products and quotients of such numbers, too.

# Examples
-i+1
\3-\2
1/4\2+1/4\6-1/4\2i+1/4\6i # a primitive 24th root of unity

Sqrum implements the Operand, Scalable, Splittable, Complex (and hence Soup, Group, Sing, Ring, Field) traits.

function meaning
add classical addition
arg complex argument
con complex conjugate
inv reciprocal
neg classical negative
mul classical multiplication
scale multiplication with a rational
split list of additive terms

The reciprocal of a non-zero number of type Sqrum could always be of the same type in theory. However, the number of additive terms grows exponentially under this operation in general. This is impractical. The necessary computation time grows even faster. The Quirl interpreter will hence return a result of type Float instead, if the inv function is applied to a number of type Sqrum with many additive terms.

Float

The Float type stores complex numbers with limited precision as floating-point real and imaginary parts. Each floating-point part is represented as a finite decimal fraction, optionally multiplied or divided by a power of ten. The optional multiplication or division by a power of ten is written as *10^ or /10^ followed by an unsigned integer exponent respectively.

Values of type float always begin with an exclamation mark ! as imprecision warning. Then comes the real part followed by the imaginary part with the imaginary unit i at the end. A zero real or imaginary part do not have to be written, if the other part is present. It is fine to write just i for 1i.

# Examples
!1.4142135623730951  # approximately the square root of 2
!0.2289156626506024i # approximately 19/83 on the imaginary axis
!-0.825-i            # an approximate complex number
!1*10^100            # approximately a googol
!1.602176634/10^19   # approximately the elementary charge in coulomb

There are multiple reasons for having an imprecise type. The value of most numbers can not be stored exactly as a finite decimal or represented as polynomial root either. Precise calculations are sometimes too slow and we only need a close approximation. Sometimes, an imprecise decimal lets us get a better grasp on a number than a more complicated exact expression. You can convert any number to Float with the float function.

Float implements the Operand, Rootable, Scalable, Complex (and hence Soup, Group, Sing, Ring, Field) traits.

function meaning
add classical addition
arg complex argument
con complex conjugate
inv reciprocal
neg classical negative
mul classical multiplication
root an n-th root
scale multiplication with a rational

Angles

There are two angular types: Turn and Circ. The former is guaranteed to be precise, the latter not. Angles of type Turn can always be converted to the Circ type. Conversion is not possible in the other direction.

Turn

The Turn type is for angles that can be expressed as a proper fraction of a whole turn. Turn is also the name of the angular unit used by Quirl. It is symbolized by the small letter t. Hence an angle of type Turn is written as a rational number followed by t. But you can also input angles in degrees. is the same as 1/360t. Mind that angles get normalized into the range from 0t inclusive to 1t exclusive.

# Examples
 1/4t       # right angle
 1/1296000t # an arcsecond
-2/3t       # same as 1/3t
 4.5t       # same as 1/2t
 270°       # same as 3/4t

Turn implements the Operand, Angle, Ordered, Scalable, Group (and hence Soup) traits.

function meaning
add addition modulo one turn
neg explement angle (modulo one turn)
ord smaller than or equal to (modulo one turn)
scale multiplication by a rational modulo one turn

Circ

The Circ type uses a real floating-point number to express an angle approximately as part of a whole turn. It begins with an exclamation mark followed by a finite decimal, optionally multiplied or divided by a power of ten, and the letter t for the angular unit turn. Alternatively, you can input angles in degrees. Mind that angles get normalized into the range from !0t inclusive to !1t exclusive.

# Examples
!0.25t                   # approximately a right angle
!7.716049382716049/10^7t # approximately an arcsecond
!4.5t                    # approximately same as !0.5t
!-15°                    # approximately same as !0.9583333333333334t

Circ implements the Operand, Angle, Ordered, Scalable, Group (and hence Soup) traits.

function meaning
add addition modulo one turn
neg explement angle (modulo one turn)
ord smaller than or equal to (modulo one turn)
scale multiplication by a rational modulo one turn

Residue classes

Modp

The Modp type is for integers modulo a prime number. Its values are written as integer followed by % and followed by an unsigned prime number, the modulus. Modp is a branded type with the modulus as brand.

# Examples
 0%2
-223%11            # same as 8%11
 15523866%20050603
 1000%2147483647

Each value of this type represents a residue class with infinitely many integer elements. Quirl chooses the smallest nonnegative integer from the class as representative. For instance, 7%3 is a valid input, but Quirl represents the class as 1%3.

Modp implements the traits Operand, Field (and hence Ring, Sing, Group, Soup).

function meaning
add addition modulo a prime
inv multiplicative inverse modulo a prime
mul multiplication modulo a prime
neg additive inverse modulo a prime

Modn

The Modn type is for integers modulo a composite number or modulo one. Its values are written as integer followed by % and followed by an unsigned integer that is not prime, the modulus.

# Examples
 0%1
-223%10          # same as 7%10
 3337%20180924
 1000%2147483645

The notation is the same as for the Modp type. Like Modp, Modn is a branded type with the modulus as brand.

Modn implements the traits Operand, Ring (and hence Sing, Group, Soup).

function meaning
add addition modulo a composite number or one
mul multiplication modulo a composite number or one
neg additive inverse modulo a composite number or one

Bracketed collections

Set

A set is a collection of distinct elements. It is not an error to write the same thing more than once into the set, but only one instance will remain. The order of elements in the set does not matter and is not preserved in Quirl. A set is wrapped in curly brackets.

# Examples
{}             # the empty set
{"tuk", "tuk"} # same as {"tuk"}
{x^3, 1/2t}    # same as {1/2t, x^3}
{{{}}}         # set that contains the set of empty sets
{-1, -i, 1, i} # set of the 4th roots of unity

Set implements the Operand, Peelable, Sing (and hence Soup) traits. The meanings of the related core functions:

function meaning
add union
mul intersection
peel list of elements

Tuple

A tuple is a list of elements wrapped in square brackets. The order of elements matters and the same value can occur multiple times in a tuple.

# Examples
[]               # the empty tuple
["tuk", "tuk"]   # NOT the same as ["tuk"]
[x^3, 1/2t]      # NOT the same as [1/2t, x^3]
[Text, add, ""]
[[1/2, -1/2\3], [1/2\3, 1/2]]

Tuple implements the Operand and Peelable traits. The meaning of the related core function:

function meaning
peel list of elements

Callables

Fun

The Fun type is for named functions. A name can consist of either Latin or Greek or Cyrillic letters and the underscore character _. The convention is to only use small letters in function names. Unlike constants, functions can be referenced by their name even before they are defined. This is necessary for indirect recursion. A consequence is that any undefined name will be of type Fun.

# Examples
add        # the name of a function
add(1 2)   # a function call with two arguments

A function needs to be defined before it is called with a full list of resolved arguments though. The tutorial describes how functions are defined.

Fun implements the Operand and Callable traits.

Comp

Comp is the type of composite functions.

Comp implements the Operand and Callable traits.

App

App is the type of partial applications.

App implements the Operand and Callable traits.

Pol

The Pol type is for univariate polynomials with rational coefficients. Univariate means that there is only one indeterminate, x. The indeterminate x can be raised to an unsigned integer exponent with ^ as power sign in between. A rational coefficient comes before x. The coefficient 1 does not need to be written before x. Likewise, x^0 does not need to be written after a coefficient. Coefficient and power of x form a monomial. Multiple monomials can be added in a polynomial.

# Examples
0            # the zero polynomial
0.75         # same as 3/4x^0
x^3-7x^2+12x # a cubic polynomial
-12/5x^8-3   # an octic polynomial

The degree of a polynomial is its greatest exponent of x after a non-zero coefficient. You can append an argument in parentheses to a polynomial of degree one or higher to evaluate it at this spot.

# Examples
x^3-7x^2+12x(5)    #  10
-12/5x^8-3(\2)     # -207/5
8x^3-x+1(0.5x^2)   #  x^6-1/2x^2+1
x^2-x-1(1/2+1/2\5) #  0

Consider to assign the polynomial to a name and to call that name, if you find the above notation confusing.

p = x^3-7x^2+12x;
p(5) # 10

Pol implements the traits Callable, Operand, Scalable, Splittable, Ring (and hence Sing, Group, Soup) traits. Here are the meanings of the related core functions:

function meaning
add polynomial addition
mul polynomial multiplication
neg additive inverse
scale multiplication by a rational
split list of monomials

Pol also sponsors the deg core map. It returns the degree of a given polynomial. The degree of the zero polynomial is defined to be -&.

Various

Beyond

The Beyond type has only two possible values -& and &. They represent values beyond any other value in the negative and positive direction of the number line respectively. You may call them negative and positive infinity. & can also be written as +&.

Beyond only implements the Operand trait.

Bool

The Bool type is for truth values. It has only two possible values, T for true and F for false.

Bool implements the traits Operand, Ordered, Rootable, Sing – and hence also Soup. The meanings of the related core functions are:

function meaning
add disjunction (OR)
mul conjunction (AND)
ord material conditional (IMPLY)
root identity

Text

The Text type would be called string in many other programming languages. It is for text. Text is wrapped in quotation marks " and can contain any valid UTF-8 character. A quotation mark inside text must be doubled up in order not to get confused for the ending delimiter though.

# Examples
"Lorem ipsum dolor sit amet,
consectetur adipiscing elit,
sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua."
"She said, ""Love don't come easy – It's a game of give and take"""

Quirl also recognizes a Unicode code point written as U+ followed by an uppercase hexadecimal number as single-character text. As usual the hexadecimal number must be at least four digits wide. Pad them with 0 on the left, if necessary.

U+00E4  # same as "ä"
U+1F955 # same as "🥕"
U+0022  # same as """"

Text implements the traits Operand, Ordered, Soup and Splittable. The meanings of the related core functions are:

function meaning
add concatenation
ord lexically ascending ordered
split list of characters

Lang

The Lang type is for context-free languages. Context-free languages are typically identfied by a name that is a nonterminal symbol from a production rule in Quirl. The page on context-free grammars explains this in detail. A name can consist of either Latin or Greek or Cyrillic letters and the underscore character _.

The only literal values of type Lang are Unicode code point ranges:

# Examples
U+0000..U+007F   # ASCII character
U+16A0..U+16F8   # Runic
U+1F030..U+1F093 # domino tile

Lang implements the Operand trait.

Type

Type implements the Operand, Sing (and hence Soup) traits. The meanings of the related core functions:

function meaning
add union type
mul intersection type

Type also sponsors the type core function which returns the type of a given operand.

Non-operand types

There are four more built-in types: List, Const, Struct and Wildcard. These types are non-operand types though: it would not make sense to use these as parameter types when defining a function.

List

A list is just items separated by space – and commas count as space, too. 1, 2, 3 is a list. Lists are flat: inserting a list into another list results in a single list consisting of both original lists' items. It does not result in one list nested inside another list. Therefore List does not make sense as parameter type in a parameter list. A corresponding argument list of a function call will never contain another list that could be matched by the parameter.

A list can be wrapped in square brackets to become a solid item that can be contained in a list. Wrapping it in square brackets makes it a Tuple though.

Const

This type manages constants. When a constant is referenced, its type is the type of its value, however. For example HELLO = "Moin!"; defines a constant HELLO, but type(HELLO) would return Text, because "Moin!" is a text.

Struct

This type manages custom data structures. A value of a custom data structure has the name of its particular structure as its type, however, not Struct. For example, Coords[Int Int] =; defines a custom data structure, but type(Coords[2, -1]) would return Coords.

Wildcard

This is the type of the monadic and variadic wildcards ? and ...?. They do not represent themselves in the argument list of a function call, however. They stand for yet undetermined values. The function call is only applied partially then and returns an anonymous function that still expects arguments in place of the wildcards.

For example, a call mod(? 10) does not pass the wildcard ? to the mod function. Instead, mod(? 10) itself becomes a function that expects a single argument. Only when that additional argument is supplied, e.g. the number 18 in mod(? 10)(18), will the mod function be applied fully to the two arguments 18 and 10 of type Int.

Additional Information

What are branded types?

Values of a branded type are not generally compatible with each other unless they are also of the same brand. You can imagine a branded type as a supertype that is divided into potentially infinitely many subtypes whose characteristic property is called brand.

For example, the type Modp is for all residue classes of integers modulo a prime number. The prime number is the brand here: we can do computations with different residue classes of integers modulo the same prime number, but operations that combine residue classes of integers modulo different prime numbers are not supported – and wouldn't make sense most of the time.

Branded types may or may not have one neutral brand whose values are compatible with all other brands. The aforementioned type Modp does not have a neutral brand, whereas the Quad type does have a neutral brand.