{"id":37136195,"url":"https://github.com/fadion/aria","last_synced_at":"2026-01-14T15:52:42.129Z","repository":{"id":57525395,"uuid":"97406355","full_name":"fadion/aria","owner":"fadion","description":"Expressive, noiseless, interpreted, toy programming language","archived":false,"fork":false,"pushed_at":"2017-08-05T21:16:21.000Z","size":1284,"stargazers_count":44,"open_issues_count":0,"forks_count":8,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-06-20T11:59:02.200Z","etag":null,"topics":["golang","interpreter","language","lexer","parser"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fadion.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-07-16T19:34:14.000Z","updated_at":"2024-05-26T14:29:16.000Z","dependencies_parsed_at":"2022-09-11T20:00:10.532Z","dependency_job_id":null,"html_url":"https://github.com/fadion/aria","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/fadion/aria","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fadion%2Faria","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fadion%2Faria/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fadion%2Faria/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fadion%2Faria/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fadion","download_url":"https://codeload.github.com/fadion/aria/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fadion%2Faria/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28425433,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T15:24:48.085Z","status":"ssl_error","status_checked_at":"2026-01-14T15:23:41.940Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","interpreter","language","lexer","parser"],"created_at":"2026-01-14T15:52:41.375Z","updated_at":"2026-01-14T15:52:42.119Z","avatar_url":"https://github.com/fadion.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GoDoc](https://godoc.org/github.com/fadion/aria?status.svg)](https://godoc.org/github.com/fadion/aria)\n[![Go Report Card](https://goreportcard.com/badge/github.com/fadion/aria)](https://goreportcard.com/report/github.com/fadion/aria)\n\n# Aria Language\n\nAria is an expressive, interpreted, toy language built as an exercise on designing and interpreting a programming language. It has a noiseless syntax, free of useless semi colons, braces or parantheses, and treats everything as an expression. Technically, it's built with a hand written lexer and parser, a recursive decent one (Pratt), and a tree-walk interpreter. I have never set any goals for it to be either fast, nor bulletproof, so don't expect neither of them.\n\nIt features mutable and immutable values, if and switch conditionals, functions, type hinting, for loops, modules, the pipe operator, imports and many more. More importantly, it's getting expanded frequently with new features, more functions for the standard library and bug fixes. All of that while retaining it's expressiveness, clean syntax and easy of use.\n\n```swift\nvar name = \"aria language\"\nlet expressive? = func (x: String) -\u003e String\n  if x != \"\"\n    return \"expressive \" + x\n  end\n  \"sorry, what?\"\nend\n\nlet pipe = name |\u003e expressive?() |\u003e String.capitalize()\nprintln(pipe) // \"Expressive Aria Language\"\n```\n\n## Table of Contents\n\n* [Usage](#usage)\n    * [Run a Source File](#run-a-source-file)\n    * [REPL](#repl)\n* [Variables](#variables)\n    * [Constants](#constants)\n    * [Type Lock](#type-lock)\n* [Data Types](#data-types)\n    * [String](#string)\n    * [Atom](#atom)\n    * [Int](#int)\n    * [Float](#float)\n    * [Boolean](#boolean)\n    * [Array](#array)\n    * [Dictionary](#dictionary)\n    * [Nil](#nil)\n    * [Type Conversion](#type-conversion)\n    * [Type Checking](#type-checking)\n* [Operators](#operators)\n    * [Shorthand Assignment](#shorthand-assignment)\n* [Functions](#functions)\n    * [Type Hinting](#type-hinting)\n    * [Default Parameters](#default-parameters)\n    * [Return Statement](#return-statement)\n    * [Variadic](#variadic)\n    * [Arrow Functions](#arrow-functions)\n    * [Closures](#closures)\n    * [Recursion](#recursion)\n    * [Tricks](#tricks)\n* [Conditionals](#conditionals)\n    * [If](#if)\n    * [Ternary Operator](#ternary-operator)\n    * [Switch](#switch)\n    * [Pattern Matching](#pattern-matching)\n* [For Loop](#for-loop)\n* [Range Operator](#range-operator)\n* [Pipe Operator](#pipe-operator)\n* [Immutability](#immutability)\n* [Modules](#modules)\n* [Imports](#imports)\n* [Comments](#comments)\n* [Standard Library](#standard-library)\n\n## Usage\n\nIf you want to play with the language, but have no interest in toying with its code, you can download a built binary for your operating system. Just head to the [latest release](https://github.com/fadion/aria/releases/latest) and download one of the archives.\n\nThe other option, where you get to play with the code and run your changes, is to `go get github.com/fadion/aria` and install it as a local binary with `go install`. Obviously, you'll need `GOROOT` in your path, but I guess you already know what you're doing.\n\n### Run a source file\n\nTo run an Aria source file, give it a path relative to the current directory.\n\n```\naria run path/to/file.ari\n```\n\n### REPL\n\nAs any serious language, Aria provides a REPL too:\n\n```\naria repl\n```\n\n## Variables\n\nVariables in Aria start with the keyword `var`. Accessing an undeclared variable, in contrast with some languages, will not create it, but instead throw a runtime error.\n\n```swift\nvar name = \"John\"\nvar married = false\n\nvar age = 40\nage = 41\n```\n\nNames have to start with an alphabetic character and continue either with alphanumeric, underscores, questions marks or exclamation marks. When you see a question mark, don't confuse them with optionals like in some other languages. In here they have no special lexical meaning except that they allow for some nice variable names like `is_empty?` or `do_it!`.\n\n### Constants\n\nConstants have the same traits as variables, except that they start with `let` and are immutable. Once declared, reassigning a constant will produce a runtime error. Even data structures are locked into immutability. Elements of an Array or Dictionary can't be added, updated or removed.\n\n```swift\nlet name = \"Ben\"\nname = \"John\" // runtime error\n```\n\n### Type Lock\n\nType lock is a safety feature of mutable variables. Once they're declared with a certain data type, they can only be assigned to that same type. This makes for more predictable results, as an integer variable can't be assigned to a string or array. In this regard, Aria works as a strong typed language.\n\nThis will work:\n\n```swift\nvar nr = 10\nnr = 15\n```\n\nThis won't:\n\n```swift\nvar nr = 10\nnr = \"ten\" // runtime error\n```\n\n## Data Types\n\nAria supports 7 data types: `String`, `Atom`, `Int`, `Float`, `Bool`, `Array`, `Dictionary` and `Nil`.\n\n### String\n\nStrings are UTF-8 encoded, meaning that you can stuff in there anything, even emojis.\n\n```swift\nlet weather = \"Hot\"\nlet price = \"円500\"\n```\n\nString concatenation is handled with the `+` operator. Concats between a string and another data type will result in a runtime error.\n\n```swift\nlet name = \"Tony\" + \" \" + \"Stark\" \n```\n\nAdditionally, strings are treated as enumerables. They support subscripting and iteration in `for in` loops.\n\n```swift\n\"howdy\"[2] // \"w\" \n```\n\nEscape sequences are there too if you need them: `\\\"`, `\\n`, `\\t`, `\\r`, `\\a`, `\\b`, `\\f` and `\\v`. Nothing changes from other languages, so I'm sure you can figure out by yourself what every one of them does.\n\n```swift\nlet code = \"if(name == \\\"ben\\\"){\\n\\tprint(10)\\n}\"\n```\n\n### Atom\n\nAtoms, or symbols as some languages refer to them, are constants where the name is their value. Although they behave a lot like strings and can generally be interchanged, internally they are treated as their own type. As the language progresses, Atoms will be put to better use.\n\n```swift\nlet eq = :dog == :cat\nlet arr = [\"dog\", :cat, :mouse]\nlet dict = [:name =\u003e \"John\", :age =\u003e 40]\nlet concat = \"hello\" + :world\n```\n\nThey're interesting to use as control conditions, emulating enums as a fixed, already-known value:\n\n```swift\nlet os = \"linux\"\nswitch os\ncase :linux\n  println(\"FREE\")\ncase :windows\n  println(\"!FREE\")\nend\n```\n\n### Int\n\nIntegers are whole numbers that support most of the arithmetic and bitwise operators, as you'll see later. They can be represented also as: binary with the 0b prefix, hexadecimal with the 0x prefix and octal with the 0o prefix.\n\n```swift\nlet dec = 27\nlet oct = 0o33\nlet hex = 0x1B\nlet bin = 0b11011\nlet arch = 2 ** 32\n```\n\nA sugar feature both in Integer and Float is the underscore:\n \n```swift\nlet big = 27_000_000\n```\n\nIt has no special meaning, as it will be ignored in the lexing phase. Writing `1_000` and `1000` is the same thing to the interpreter.\n\n### Float\n\nFloating point numbers are used in a very similar way to Integers. In fact, they can be mixed and matched, like `3 + 0.2` or `5.0 + 2`, where the result will always be a Float.\n\n```swift\nlet pi = 3.14_159_265\nlet e = 2.71828182\n```\n\nScientific notation is also supported via the `e` modifier:\n\n```swift\nlet sci = 0.1e3\nlet negsci = 25e-5\n```\n\n### Bool\n\nIt would be strange if this data type included anything else except `true` and `false`.\n\n```swift\nlet mad = true\nlet genius = false\n```\n\nExpressions like the `if/else`, as you'll see later, will check for values that aren't necessarily boolean. Integers and Floats will be checked if they're equal to 0, and Strings, Arrays and Dictionaries if they're empty. These are called `truthy` expressions and internally, will be evaluated to boolean.\n\n### Array\n\nArrays are ordered collections of any data type. You can mix and match strings with integers, or floats with other arrays.\n \n```swift\nlet multi = [5, \"Hi\", [\"Hello\", \"World\"]]\nlet names = [\"John\", \"Ben\", 1337]\n\nlet john = names[0]\nlet leet = names[-1]\n```\n \nIndividual array elements can be accessed via subscripting with a 0-based index:\n\n```swift\nlet names = [\"Kirk\", \"Bones\", \"Spock\"]\nlet first = names[0] // \"Kirk\"\nlet last = names[-1] // \"Spock\"\n```\n\nIn the same style, an index can be used to check if it exists. It will return `nil` if it doesn't:\n\n```swift\nif names[10]\n  // handle it\nend\n```\n\nIndividual elements can be reassigned on mutable arrays:\n\n```swift\nvar numbers = [5, 8, 10, 15]\nnumbers[1] = 7\n```\n\nAppended with an empty or placeholder index:\n\n```swift\nnumbers[] = 100\nnumbers[_] = 200 // Same.\n```\n\nArrays can be compared with the `==` and `!=` operators, which will check the position and value of every element of both arrays. Equal arrays should have the same exact values in the same position.\n\nThey can also be combined with the `+` operator, which adds the element of the right side to the array on the left side.\n\n```swift\nlet concat = [\"an\", \"array\"] + [\"and\", \"another\"]\n// [\"an\", \"array\", \"and\", \"another\"]\n```\n\nOh and if you're that lazy, you can ommit commas too:\n\n```swift\nlet nocomma = [5 7 9 \"Hi\"]\n```\n \n### Dictionary\n \nDictionaries are hashes with a key and a value of any data type. They're good to hold unordered, structured data:\n\n```swift\nlet user = [\"name\" =\u003e \"Dr. Unusual\", \"proffesion\" =\u003e \"Illusionist\", \"age\" =\u003e 150]\n```\n\nI'd argue that using Atoms for keys would make them look cleaner:\n\n```swift\nlet user = [:name =\u003e \"Dr. Unusual\", :proffesion =\u003e \"Illusionist\", :age =\u003e 150]\n```\n\nUnlike arrays, internally their order is irrelevant, so you can't rely on index-based subscripting. They only support key-based subscripting:\n \n```swift\nuser[\"name\"] // \"Dr. Unusual\"\n```\n\nValues can be reassigned or inserted by key on mutable dictionaries:\n\n```swift\nvar numbers = [\"one\" =\u003e 1, \"two\" =\u003e 2]\nnumbers[\"one\"] = 5\nnumbers[\"three\"] = 3 // new key:value\n```\n\nTo check for a key's existence, you can access it as normal and check if it's `nil` or truthy:\n\n```swift\nif user[\"location\"] == nil\n  // do smth\nend\n```\n\n### Nil\n\nAria has a Nil type and yes, I'm totally aware of its problems. This was a choice for simplicity, at least for the time being. In the future, I plan to experiment with optionals and hopefully integrate them into the language.\n\n```swift\nlet empty = nil\n```\n\n### Type Conversion\n\nConverting between types is handled in a few ways that produce exactly the same results. The `as` operator is probably the more convenient and more expressive of the bunch. Like all type conversion methods, it can convert to `String`, `Int`, `Float` and `Array`:\n\n```swift\nlet nr = 10\nnr as String\nnr as Int\nnr as Float\nnr as Array\n```\n\nProvided by the runtime are the appropriately named functions: `String()`, `Int()`, `Float()` and `Array()`.\n\n```swift\nlet str = String(10)\nlet int = Int(\"10\")\nlet fl = Float(10)\nlet arr = Array(10)\n```\n\nThe `Type` module of the Standard Library provides interfaces to those same functions and even adds some more, like `Type.of()` and `Type.isNumber()`.\n\n```swift\nlet str = Type.toString(10)\nlet int = Type.toInt(\"10\")\nlet fl = Type.toFloat(10)\nlet arr = Type.toArray(10)\n```\n\nWhich method you choose to use is strictly preferential and depends on your background.\n\n### Type Checking\n\nThere will be more than one occassion where you'll need to type check a variable. Aria provides a few ways to achieve that.\n\nThe `is` operator is specialized in checking types and should be the one you'll want to use practically everywhere.\n\n```swift\nlet nr = 10\nif nr is Int\n  println(\"Yes, an integer\")\nend\n```\n\nThere's also the `typeof()` runtime function and `Type.of()` from the Standard Library. They essentially do the same thing, but not only they're longer to write, but return strings. The above would be equivalent to:\n\n```swift\nif Type.of(nr) == \"Int\"\n  println(\"Yes, an integer\")\nend\n```\n\n## Operators\n\nYou can't expect to run some calculations without a good batch of operators, right? Well, Aria has a range of arithmetic, boolean and bitwise operators to match your needs.\n\nBy order of precedence:\n\n```swift\nBoolean: \u0026\u0026 || (AND, OR)\nBitwise: \u0026 | ~ (Bitwise AND, OR, NOT)\nEquality: == != (Equal, Not equal)\nComparison: \u003c \u003c= \u003e \u003e=\nBitshift: \u003c\u003c \u003e\u003e (Bitshift left and right)\nArithmetic: + - * / % ** (addition, substraction, multiplication, division, modulo, power)\n```\n\nArithmetic expressions can be safely used for Integers and Floats:\n\n```swift\n1 + 2 * 3 / 4.2\n2 ** 8\n3 % 2 * (5 - 3)\n```\n\nAddition can be used to concatenate Strings or combine Arrays and Dictionaries:\n\n```swift\n\"obi\" + \" \" + \"wan\"\n[1, 2] + [3, 4]\n[\"a\" =\u003e 1, \"b\" =\u003e 2] + [\"c\" =\u003e 3]\n```\n\nComparison operators can compare Integers and Float by exact value, Strings, Arrays and Dictionaries by length:\n\n```swift\n5 \u003e 2\n3.2 \u003c= 4.5\n\"one\" \u003c \"three\"\n[1, 2] \u003e [5]\n[\"a\" =\u003e 1] \u003c [\"b\" =\u003e 2, \"c\" =\u003e 3]\n```\n\nEquality and inequality can be used for most data types. Integers, Floats and Booleans will be compared by exact value, Strings by length, Arrays by the value and position of the elements, and Dictionaries by the the combination of key and value.\n\n```swift\n1 != 4\n1.0 != 2.5\ntrue == true\n\"one\" == \"three\"\n[1, 2, 3] != [1, 2]\n[\"a\" =\u003e 1, \"b\" =\u003e 2] != [\"a\" =\u003e 5, \"b\" =\u003e 6]\n```\n\nBoolean operators can only be used with Boolean values, namely `true` or `false`. Other data types will not be converted to truthy values.\n\n```swift\ntrue == true\nfalse != true\n```\n\nBitwise and bitshift operator apply only to Integers. Float values can't be used, even those that \"look\" as Integers, like `1.0` or `5.0`.\n\n```swift\n10 \u003e\u003e 1\n12 \u0026 5 | 3\n5 ~ 2\n```\n\n### Shorthand Assignment\n\nOperators like `+`, `-`, `*` and `/` support shorthand assignment to variables. Basically, statements like this:\n\n```swift\ncount = count + 1\n```\n\nCan be expressed as:\n\n```swift\ncount += 1\n```\n\n## Functions\n\nAria treats functions as first class, like any sane language should. It checks all the boxes: they can be passed to variables, as arguments to other functions, and as elements to data structures. They also support recursion, closures, currying, variadic parameters, you name it.\n\n```swift\nlet add = func x, y\n  x + y\nend\n```\n\nParantheses are optional and for simple functions like the above, I'd omit them. Calling the function needs the parantheses though:\n\n```swift\nlet sum = add(1335, 2)\n```\n\n### Type Hinting\n\nLike in strong typed languages, type hinting can be a very useful feature to validate function arguments and its return type. It's extra useful for library functions that have no assurance of the data types they're going to get.\n\nThis function call will produce output:\n\n```swift\nlet add = func (x: Int, y: Int) -\u003e Int\n  x + y\nend\nprintln(add(5, 2))\n```\n\nThis however, will cause a type missmatch runtime error:\n\n```swift\nprintln(add(5, \"two\"))\n```\n\nAria is not a strong typed language, so type hinting is completely optional. Generally, it's a good idea to use it as a validation measure. Once you enforce a certain type, you'll be sure of how the function executes.\n\n### Default Parameters\n\nFunction parameters can have default values, used when the parameters are omitted from function calls.\n\n```swift\nlet architecture = func bits = 6\n  2 ** bits\nend\n\narchitecture() // 64\narchitecture(4) // 16 \n```\n\nThey can be combined with type hinting and, obviously, need to be of the same declared type.\n\n```swift\nlet architecture = func bits: Int = 6\n  2 ** bits\nend\n```\n\n### Return Statement\n\nUntil now we haven't seen a single `return` statement. Functions are expressions, so the last line is considered its return value. In most cases, especially with small functions, you don't have to bother. However, there are scenarios with multiple return points that need to explicitly tell the interpreter.\n\n```swift\nlet even = func n\n  if n % 2 == 0\n    return true\n  end\n  false\nend\n``` \n\nThe last statement doesn't need a `return`, as it's the last line and will be automatically inferred. With the `if` on the other hand, the interpreter can't understand the intention, as it's just another expression. It needs the explicit `return` to stop the other statements from being interpreted.\n\nIn the case of multiple return points, I'd advise to always use `return`, no matter if it's the first or last statement. It will make for clearer intentions. \n\n### Variadic\n\nVariadic functions take an indefinite number of parameters and merge them all into a single, Array argument. Their first use would be as a sugar:\n\n```swift\nlet add = func ...nums\n  var count = 0\n  for n in nums\n    count = count + n\n  end\n  count\nend\n\nadd(1, 2, 3, 4, 5) // 10\n```\n\nEven better, they can be used for functions that respond differently based on the number of arguments:\n\n```swift\nlet structure = func ...args\n  if Enum.size(args) == 2\n    let key = args[0]\n    let val = args[1]\n    return [key: val]\n  end\n  if Enum.size(args) \u003e 2\n    return args\n  end\n  args[0]\nend\n\nstructure(\"name\", \"John\") // dictionary\nstructure(1, 2, 3) // array\nstructure(5) // integer\n```\n\nFunctions may have as many parameters as needed, as long the variadic argument is the last parameter:\n\n```swift\nlet calc = func mult, ...nums\n  mult * Enum.reduce(nums, 0, func x, acc do x + acc end)\nend\ncalc(10, 1, 2, 3, 4) // 100\n```\n\nVariadic arguments can even have default values:\n\n```swift\nlet join = func (glue: String, ...words = [\"hello\", \"there\"])\n  String.join(words, glue)\nend\n\njoin(\" \") // \"hello there\"\n```\n\n### Arrow Functions\n\nVery useful when passing short functions as arguments, arrow functions provide a very clean syntax. They're handled internally exactly like normal functions. The only difference is that they're meant as a single line of code, while normal functions can handle blocks.\n\nThis normal function:\n\n```swift\nlet sub = func x\n  x - 5\nend\n```\n\nIs equivalent to:\n\n```swift\nlet sub = (x) -\u003e x - 5\n```\n\nThey're not that useful to just spare a couple lines of code. They shine when passed as arguments:\n\n```swift\nEnum.map([1, 2, 3, 4], (x) -\u003e x * 2)\nEnum.reduce(1..10, 0, (x, acc) -\u003e x + acc)\n```\n\n### Closures\n\nClosures are functions inside functions that hold on to values from the parent and \"close\" them when executed. This allows for some interesting side effects, like currying:\n\n```swift\nlet add = func x\n  func y\n    x + y\n  end\nend\n\nadd(5)(7) // 12\n```\n\nSome would prefer a more explicit way of calling:\n\n```swift\nlet add_5 = add(5) // returns a function\nlet add_5_7 = add_5(7) // 12\n```\n\nYou could nest a virtually unlimited amount of functions inside other functions, and all of them will have the scope of the parents.\n\n### Recursion\n\nRecursive functions calculate results by calling themselves. Although loops are probably easier to mentally visualize, recursion provides for some highly expressive and clean code. Technically, they build an intermediate stack and rewind it with the correct values in place when a finishing, non-recursive result is met. It's easier to understand them if you think of how they're executed. Let's see the classic factorial example:\n\n```swift\nlet fac = func n\n  if n == 0\n    return 1\n  end\n  \n  n * fac(n - 1)\nend\n``` \n\nKeep in mind that Aria doesn't provide tail call optimization, as Go still doesn't support it. That would allow for more memory efficient recursion, especially when creating large stacks.\n\n### Tricks\n\nAs first class, functions have their share of tricks. First, they can self-execute and return their result immediately:\n\n```swift\nlet pow_2 = func x\n  x ** 2\nend(2)\n```\n\nNot sure how useful, but they can be passed as elements to data structures, like arrays and dictionaries:\n\n```swift\nlet add = func x, y do x + y end\nlet list = [1, 2, add]\nlist[2](5, 7) \n```\n\nFinally, like you may have guessed from previous examples, they can be passed as parameters to other functions:\n\n```swift\nlet add = func x, factor\n  x + factor(x)\nend\nadd(5, (x) -\u003e x * 2)\n```\n\n## Conditionals\n\nAria provides two types of conditional statements. The `if/else` is limited to just an `if` and/or `else` block, without support for multiple `else if` blocks. That's because it advocates the use of the much better looking and flexible `switch` statement.\n\n### If\n\nAn `if/else` block looks pretty familiar:\n\n```swift\nif 1 == 2\n  println(\"Not calling me.\")\nelse\n  println(\"1 isn't equal to 2. Duh!\")\nend\n```\n\nSometimes it's useful to inline it for simple checks:\n\n```swift\nlet married = true\nlet free_time = if married then 0 else 100_000_000 end\n```\n\n### Ternary Operator\n\nThe ternary operator `?:` is a short-hand `if/else`, mostly useful when declaring variables based on a condition or when passing function parameters. It's behaviour is exactly as that of an `if/else`.\n\n```swift\nlet price = 100\nlet offer = 120\nlet status = offer \u003e price ? \"sold\" : \"bidding\"\n```\n\nAlthough multiple ternary operators can be nested, I wouldn't say that would be the most readable code. Actually, except for simple checks, it generally makes for unreadable code.\n\n### Switch\n\n`Switch` expressions on the other hand are way more interesting. They can have multiple cases with multiple conditions that break automatically on each successful case, act as generic if/else, and match array elements.\n\n```swift\nlet a = 5\nswitch a\ncase 2, 3\n  println(\"Is it 2 or 3?\")\ncase 5\n  println(\"It is 5. Magic!\")\ndefault\n  println(\"No idea, sorry.\")\nend\n```\n\nNot only that, but a `switch` can behave as a typical if/else when no control condition is provided. It basically becomes a `switch true`.\n\n```swift\nlet a = \"John\"\nswitch\ncase a == \"John\"\n  println(\"John\")\ncase a == \"Ben\"\n  println(\"Ben\") \ndefault\n  println(\"Nobody\")\nend\n```\n\n### Pattern Matching\n\nWhen fed arrays as the control condition, the `switch` can pattern match its elements. Every argument to the switch case is compared to the respective element of the array. Off course, for a match, the number of arguments should match the size of the array.\n\n```swift\nswitch [\"game\", \"of\", \"thrones\"]\ncase \"game\", \"thrones\"\n  println(\"no match\")\ncase \"game\", \"of\", \"thrones\"\n  println(\"yep!\")\nend\n```\n\nThat's probably useful from time to time, but it's totally achievable with array cases. The `switch` can do much better than that.\n\n```swift\nswitch [\"John\", \"Lick\", 2]\ncase \"John\", _, _\n  println(\"John Something\")\ncase _, _ 2\n  println(\"Something 2\")\ndefault\n  println(\"Lame movie pun not found\")\nend\n```\n\nThe `_` is a placeholder that will match any type and value. That makes it powerful to compare arrays where you don't need to know every element. You can mix and match values with placeholders in any position, as long as they match the size of the array.\n\n## For Loop\n\nAria takes a modern approach to the `for` loop, evading from the traditional, 3-parts `for` we've been using for decades. Instead, it focuses on a flexible `for in` loop that iterates arrays, dictionaries, and as you'll see later, ranges.\n\n```swift\nfor v in [1, 2, 3, 4]\n  println(v)\nend\n```\n\nObviously, the result of the loop can be passed to a variable, and that's what makes them interesting to manipulate enumerables.\n\n```swift\nlet plus_one = for v in [1, 2, 3, 4]\n  v + 1\nend\nprintln(plus_one) // [2, 3, 4, 5]\n```\n\nPassing two arguments for arrays or strings will return the current index and value. For dictionaries, the first argument will be the key.\n\n```swift\nfor i, v in \"abcd\"\n  println(i + \"=\u003e\" + v)\nend\n```\n\n```swift\nfor k, v in [\"name\" =\u003e \"John\", \"age\" =\u003e 40]\n  println(k)\n  println(v)\nend\n```\n\nWith that power, you could build a function like `map` in no time:\n\n```swift\nlet map = func x, f\n  for v in x\n    f(v)\n  end\nend\n\nlet plus_one = map([1, 2, 3, 4], (x) -\u003e x + 1)\n\nprintln(plus_one) // [2, 3, 4, 5]\n```\n\nWithout arguments, the `for` loop can behave as an infite loop, much like a traditional `while`. Although there's not too many usecases, it does its job when needed. An example would be prompting the user for input and only breaking the infinite loop on a specific text.\n\n```swift\nfor\n  let pass = prompt(\"Enter the password: \")\n  if pass == \"123\"\n    println(\"Good, strong password!\")\n    break\n  end\nend\n```\n\nThe `break` and `continue` keywords, well break or skip the iteration. They function exactly like you're used to.\n\n```swift\nfor i in 1..10\n  if i == 5\n    continue\n  end\nend\n```\n\n*The `for` loop is currently naively parsed. It works for most cases, but still, it's not robust enough. I'm working to find a better solution.*\n\n## Range Operator\n\nThe range operator is a special type of sugar to quickly generate an array of integers or strings. \n\n```swift\nlet numbers = 0..9\nlet huge = 999..100\nlet alphabet = \"a\"..\"z\"\n```\n\nAs it creates an enumerable, it can be put into a `for in` loop or any other function that expects an array.\n\n```swift\nfor v in 10..20\n  println(v)\nend\n```\n\nAlthough its bounds are inclusive, meaning that the left and right expressions are included in the generated array, nothing stops you from doing calculations. This is completely valid:\n\n```swift\nlet numbers = [1, 2, 3, 4]\nfor i in 0..Enum.size(numbers) - 1\n  println(i)\nend\n```\n\n## Pipe Operator\n\nThe pipe operator, inspired by [Elixir](https://elixir-lang.org/), is a very expressive way of chaining functions calls. Instead of ugly code like the one below, where the order of operations is from the inner function to the outers ones:\n\n```swift\nsubtract(pow(add(2, 1)))\n```\n\nYou'll be writing beauties like this one:\n\n```swift\nadd(2, 1) |\u003e pow() |\u003e substract()\n```\n\nThe pipe starts from left to right, evaluating each left expression and passing it automatically as the first parameter to the function on the right side. Basically, the result of `add` is passed to `pow`, and finally the result of `pow` to `substract`.\n\nIt gets even more interesting when combined with standard library functions:\n\n```swift\n[\"hello\", \"world\"] |\u003e String.join(\" \") |\u003e String.capitalize()\n```\n\nEnumerable functions too:\n\n```swift\nEnum.map([1, 2, 3], (x) -\u003e x + 1) |\u003e Enum.filter((x) -\u003e x % 2 == 1)\n\n// or even nicer\n\n[1, 2, 3] |\u003e Enum.map((x) -\u003e x + 1) |\u003e Enum.filter((x) -\u003e x % 2 == 1)\n```\n\nSuch a simple operator hides so much power and flexibility into making more readable code. Almost always, if you have a chain of functions, think that they could be put into a pipe.\n\n## Immutability\n\nNow that you've seen most of the language constructs, it's time to fight the dragon. Immutability is something you may not agree with immediately, but it makes a lot of sense the more you think about it. What you'll earn is increased clarity and programs that are easier to reason about.\n\nIterators are typical examples where mutability is seeked for. The dreaded `i` variable shows itself in almost every language's `for` loop. Aria keeps it simple with the `for in` loop that tracks the index and value. Even if it looks like it, the index and value aren't mutable, but instead arguments to each iteration of the loop.\n\n```swift\nlet numbers = [10, 5, 9]\nfor k, v in numbers\n  println(v) \n  println(numbers[k]) // same thing\nend\n```\n\nBut there may be more complicated scenarios, like wanting to modify an array's values. Sure, you can do it with the `for in` loop as we've seen earlier, but higher order functions play even better:\n\n```swift\nlet plus_one = Enum.map([1, 2, 3], (x) -\u003e x + 1)\nprintln(plus_one) // [2, 3, 4]\n```\n\nWhat about accumulators? Let's say you want the product of all the integer elements of an array (factorial) and obviously, you'll need a mutable variable to hold it. Fortunately we have `reduce`:\n\n```swift\nlet product = Enum.reduce(1..5, 1, (x, acc) -\u003e x * acc)\nprintln(product)\n```\n\nThink first of how you would write the problem with immutable values and only move to mutable ones when it's impossible, hard or counter-intuitive. In most cases, immutability is the better choice.\n\n## Modules\n\nModules are very simple containers of data and nothing more. They're not an imitation of classes, as they can't be initialized, don't have any type of access control, inheritance or whatever. If you need to think in Object Oriented terms, they're like a class with only static properties and methods. They're good to give some structure to a program, but not to represent cars, trees and cats.\n\n```swift\nmodule Color\n  let white = \"#fff\"\n  let grey = \"#666\"\n  let hexToRGB = func hex\n    // some calculations\n  end\nend\n\nlet background = Color.white\nlet font_color = Color.hexToRGB(Color.grey)\n```\n\nBecause modules are interpreted and cached before-hand, properties and functions have access to each other. In contrast to modules, everything else in Aria is single pass and as such, it will only recognize calls to a module that has already been declared.\n\n## Imports\n\nSource file imports are a good way of breaking down projects into smaller, easily digestible files. There's no special syntax or rules to imported files. They're included in the caller's scope and treated as if they were originally there. Imports are cached, so in multiple imports, only the first one is actually interpreted.\n\n```swift\n// cat.ari\nlet name = \"Bella\"\nlet hi = func x\n  \"moew \" + x\nend\n```\n\n```javascript\n// main.ari\nimport \"cat\"\n\nlet phrase = name + \" \" + hi(\"John\")\nprintln(phrase) // \"Bella moew John\"\n```\n\nThe file is relatively referenced from the caller and in this case, both `main.ari` and `dog.ari` reside in the same folder. As the long as the extension is `.ari`, there's no need to write it in the import statement. Even the quotes can be omited and the file written as an identifier, as long as it doesn't include a dot (as in `cat.ari`) and isn't a reserved keyword.\n\nA more useful pattern would be to wrap imported files into a module. That would make for a more intuitive system and prevent scope leakage. The cat case above could be written simply into:\n\n```swift\n// cat.ari\nmodule Cat\n  let name = \"Bella\"\n  let hi = func x\n    \"moew \" + x\n  end\nend\n```\n\n```javascript\n// main.ari\nimport cat\n\nlet phrase = Cat.name + \" \" + Cat.hi(\"John\")\n```\n\nImports are expressions too! Technically, they can be used anywhere else an Integer or String can, even though it probably wouldn't make for the classiest code ever.\n\n```javascript\n// exp.ari\nlet x = 10\nlet y = 15\nx + y\n```\n\n```javascript\n// main.ari\nlet value = import exp\nprintln(value) // 25\n\nif import exp == 25\n  println(\"Yay\")\nend\n```\n\n## Comments\n\nNothing ground breaking in here. You can write either single line or multi line comments:\n\n```\n// an inline comment\n/*\n  I'm spanning multiple\n  lines.\n*/\n```\n\n## Standard Library\n\nThe Standard Library is fully written in Aria with the help of a few essential functions provided by the runtime. That is currently the best source to check out some \"production\" Aria code and see what it's capable of. [Read the documentation](https://github.com/fadion/aria/wiki/Standard-Library). \n\n## Future Plans\n\nAlthough this is a language made purely for fun and experimentation, it doesn't mean I will abandon it in it's first release. Adding other features means I'll learn even more!\n\nIn the near future, hopefully, I plan to:\n\n- Improve the Standard Library with more functions.\n- ~~Support closures and recursion~~.\n- ~~Add a short syntax for functions in the form of: (x) -\u003e x~~.\n- ~~Add importing of other files~~.\n- ~~Add the pipe operator!~~\n- Support optional values for null returns.\n- Write more tests!\n- Write some useful benchmarks with non-trivial programs.\n\n## Credits\n\nAria was developed by Fadion Dashi, a freelance web and mobile developer from Tirana.\n\nThe implementation is based on the fantastic [Writing an Interpreter in Go](https://interpreterbook.com/). If you're even vaguely interested in interpreters, with Golang or not, I highly suggest that book.\n\nThe `reader.Buffer` package is a \"fork\" of Golang's `bytes.Buffer`, in which I had to add a method that reads a rune without moving the internal cursor. I hate doing that, but unfortunately couldn't find a way out of it. That package has its own BSD-style [license](https://github.com/golang/go/blob/master/LICENSE).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffadion%2Faria","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffadion%2Faria","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffadion%2Faria/lists"}