Tupilled

From Esolang
Jump to navigation Jump to search
Tupilled
Paradigm(s) Functional, High-Level
Designed by User:Proxxa, User:Ashli Katt
Appeared in Category:2023
Type system Dynamic
Computational class Unknown
Reference implementation Unimplemented
File extension(s) .tup, .tuple

Tupilled is a high-level, functional programming language based on tuples and lambdas by User:Ashli Katt and User:Proxxa. All values are tuples or lambdas, so there are no true types, and thus there is no compile-time type checking. Tupilled is highly inspired by Haskell.

The name comes from a shortening of the word "Tuple-pilled."

Syntax Outline

Reserved Tokens

Tupilled has a small set of reserved tokens.

  • (), for tuples.
  • [], as a grouping symbol. They also indicate an evaluation when used as a parameter.
  • {}, for lambda definitions or parameters.
  • =, for assigning functions.
  • ,, for separating tuple elements and function parameters.
  • // indicates a comment.

The following tokens are not reserved but instead hold special meaning.

  • main is an entry point. It must be defined in every Tupilled program.
  • panic will abruptly stop the program.
  • unique every instance is replaced with a unique tuple during parsing. This functionality is useful for user-defined "types."

The grouping symbols and commas are completely disallowed in identifiers. = can appear in an identifier so long as the identifier is not only =. Similarly, . may appear, but .. cannot. a// is an identifier, but a // is an identifier followed by a comment.

Tuples

Tuples are a list of comma-delimited elements wrapped in a set of parentheses, (). As such, () is a valid tuple as is ((), (())). Given there are no numbers in Tupilled, a simple approach to an unsigned integer is creating Peano numbers where () is 0, (()) is 1, ((())) is 2, and so on. Tuples may also contain lambdas.

Functions

Functions follow a similar definition scheme to very simple functions in Haskell.

ident paramA, paramB = body

Identifiers may be any string of characters which are not reserved. Parameters are comma-delimited and may match values as explained later. All function bodies consist of a single statement.

Functions are all equivalent to giving a lambda value to a top-level constant.

Pattern Matching

Function parameters may be pattern-matched in order to destructure arguments or set specific requirements. The parameter name (a) will match a tuple with one inner element and provide that element to the identifier a. Similarly, (a, b) will match a tuple with two inner elements and provide those elements to the identifiers a and b, respectively. An identifier on its own, such as a, will simply match any tuple. For example: snd (a, b) = b

f{a} matches any lambda that accepts one tuple parameter and names it f. a has no meaning here and is used simply as a pattern to match any tuple; it is not usable by the function's body. In a similar way, f{} accepts a lambda with no arguments. f{{}} accepts a lambda that takes another lambda (which takes nothing). {} matches a lambda with 0 arguments, and binds it to nothing. For example: map_pair f{z} (a, b) = (f a, f b)

An underscore may be used to ignore a value, so (_, a) will get the pair's second element, and ignore the first. An underscore can take the place of anything that would otherwise be a name. For example: first a, _ = a accepts two parameters, but only binds the first one.

Brackets have special meaning inside of a pattern, they may be used to match the return value of an expression. [expr] will evaluate expr and match the returned value. For example: first ([Pair], (a, _)) = a will destructure a 2-tuple, its first value must match the return value of Pair (A function), and the rest is destructed as usual.

Piecewise Functions

Functions can be given multiple definitions when all those definitions accept the same number of arguments. This is done by appending a comma to a definition and writing new parameters and a new body.

false = ()
true = (false)

isPeano ()  = true,
        (n) = isPeano n,
        _   = false

In this example, we check if a tuple is a peano number. ie: (), (()), ((())), etc.

When isPeano is called, it will attempt to match the supplied arguments to its patterns first-to-last. It will first check if the argument is the empty tuple, which is Peano 0. If that fails (not a 0-tuple), it will attempt to match the argument to the pattern (n), a 1-tuple, and recursively check if the inner element is a peano. If both of those fail, (2+ tuple), it is not a peano number.

Infixing

A function may be infixed if it takes in two arguments and the function identifier is wrapped in parentheses. As such, a definition of a && operator may be made as such:

false = ()
true = (false)

(&&) [false], _ = false,
     _      , a = a

Infix operations have lower precedence than regular prefix functions. You may need to use [] to group expressions when working with infix functions. The parser will, however, greedily match infix operations. Ex: square 2 + 3 is treated as square [2 + 3] NOT [square 2] + 3

Lambdas

Lambdas, much like they are in other languages, are like nameless functions. In reality, though, functions are named lambdas. They are defined by wrapping a function without its identifier in braces, {}. A function can be defined by providing no arguments before the = and returning a lambda.

wrap = { a = (a) }

Input/Output

All input and output are defined within the main function, which must have either 0 or 1 arguments. When the main function returns, its value is read and the following steps are taken:

  • Create a bitstring, ""
  • Match (a, b). If a is (), push a 0 to the output bitstring, otherwise push a 1.
  • Repeat the previous process using b as the new output tuple. If b's length is not 2, stop early.
  • When finished, interpret the bitstring as UTF-8 text, padding the end with 0s as necessary.

Here is an example of output being parsed:

Tuple                     | Bitstring
--------------------------------------
((()), ((), ((()), ())))  | 
((), ((()), ()))          | 1
((()), ())                | 10
()                        | 101
                          | 00000101

The input is given the same format and is done in reverse. So, if the input is 101, then the data input to main is ((()), ((), ((()), ()))). In practice, this input would be padded with 0s to become ((), ((), ((), ((), ((), ((()), ((), ((()), ()))))))))

There is no mid-program printing.

Examples

Hello World

A program that outputs Hello, World!

main = ((),((()),((),((),((()),((),((),((),((),((()),((()),((),((),((()),((),((()),((),
       ((()),((()),((),((()),((()),((),((),((),((()),((()),((),((()),((()),((),((),((),
       ((()),((()),((),((()),((()),((()),((()),((),((),((()),((),((()),((()),((),((),
       ((),((),((()),((),((),((),((),((),((),((()),((),((()),((),((()),((()),((()),((),
       ((()),((()),((),((()),((()),((()),((()),((),((()),((()),((()),((),((),((()),((),
       ((),((()),((()),((),((()),((()),((),((),((),((()),((()),((),((),((()),((),((),
       ((),((),((()),((),((),((),((),((()))))))))))))))))))))))))))))))))))))))))))
       )))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))

or, more readable:

0 a = ((), a)
1 a = ((()), a)
. = ()

main = [ 0 1 0 0 1 0 0 0 0 1 1 0 0 1 0 1 0 1 1 0 1 1 0 0 0 1 1 0 1 1 0 0 0 1 1 0 1 1 1 1 
         0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 0 1 0 1 0 1 1 1 0 1 1 0 1 1 1 1 0 1 1 1 0 0 1 0 
         0 1 1 0 1 1 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 1 . ]

Panic

A program that will always panic.

main = panic

Cat

A cat program will output its input.

main a = a

Reverse Cat

A program that will output the reverse of its input.

main a = reverse a
reverse bitstring = #reverse bitstring ()
#reverse (0, (1, (2, (3, (4, (5, (6, (7, next)))))))) last = #reverse next (0, (1, (2, (3, (4, (5, (6, (7, last))))))))
         _,                                           last = last

Boolean Operations

Implementation of simple boolean operations.

false = ()
true = (false)
bool [false] = false,
             = true

(&)  [true],  [true] = true,
                     = false
(|) [false], [false] = false,
                     = true
!            [false] = true,
                     = false
(^) [false],       x = bool x,
          _,       x = !x