{"id":14240785,"url":"https://github.com/cryptocode/bio","last_synced_at":"2025-10-13T19:40:29.617Z","repository":{"id":63488972,"uuid":"355994606","full_name":"cryptocode/bio","owner":"cryptocode","description":"A Lisp dialect written in Zig","archived":false,"fork":false,"pushed_at":"2024-09-26T08:25:37.000Z","size":1339,"stargazers_count":189,"open_issues_count":1,"forks_count":1,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-05T15:02:03.737Z","etag":null,"topics":["lisp","scheme","zig"],"latest_commit_sha":null,"homepage":"","language":"Zig","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/cryptocode.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-08T17:25:25.000Z","updated_at":"2025-03-11T03:15:07.000Z","dependencies_parsed_at":"2025-02-22T17:38:10.632Z","dependency_job_id":null,"html_url":"https://github.com/cryptocode/bio","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cryptocode%2Fbio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cryptocode%2Fbio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cryptocode%2Fbio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cryptocode%2Fbio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cryptocode","download_url":"https://codeload.github.com/cryptocode/bio/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247773721,"owners_count":20993639,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["lisp","scheme","zig"],"created_at":"2024-08-21T10:01:56.738Z","updated_at":"2025-10-13T19:40:29.591Z","avatar_url":"https://github.com/cryptocode.png","language":"Zig","readme":"\u003cimg src=\"https://user-images.githubusercontent.com/34946442/143932794-213c6eaa-76ca-4f98-985f-345d1fc8e925.png\" width=100 height=100\u003e\n\n*Why no updates lately? In short, Bio is being rewritten to target a VM for vastly improved performance, and to make embedding Bio into applications easy. The next release of Bio is planned to target Zig 0.16, which includes the new Io interface.*\n\nBio is a Lisp dialect similar to Scheme, with some novel features like environment expressions which enable capabilities like modules and composite types.\n\nFeatures include macros, garbage collection, error handling, a module facility, destructuring, and a standard library.\n\nExample:\n\n```scheme\n(filter\n    (quicksort '(5 40 1 -3 2) \u003c)\n        (lambda (x) (\u003e= x 0)))\n\n(1 2 5 40)\n```\n\nThe core of Bio is lambda expressions, from which the standard library builds syntax like `type` and `fun`:\n\n```scheme\n; Create a composite type. This is sugar for `(var Point (lambda (x y) ... (self)))`\n(type Point (x y) \n    (fun area () (* x y)))\n\n; Make an instance of Point\n(var pt (Point 5 7))\n\n; Call a member function. All this is just syntax sugar based on a lambda\n; returning its own environment.\n(print \"Area:\" (pt (area)) '\\n)\n\nArea: 35\n```\n\n*The language and its documentation is work in progress. For up-to-date examples, I recommend studying `std.lisp`, `test.lisp` and the files in the `examples` directory.*\n\nTable of Contents\n=================\n\n   * [Building and running](#building-and-running)\n      * [Running tests](#running-tests)\n   * [Language Reference](#language-reference)\n      * [Naming conventions](#naming-conventions)\n      * [Intrinsics](#intrinsics)\n         * [nil](#nil)\n         * [#t and #f](#t-and-f)\n         * [#! and #value](#-and-value)\n         * [typename](#typename)\n         * [number? symbol? list? bool? callable? error?](#number-symbol-list-bool-callable-error)\n         * [var and define](#var-and-define)\n         * [vars](#vars)\n         * [set! and unset!](#set-and-unset)\n         * [arithmetic functions](#arithmetic-functions)\n         * [equality](#equality)\n         * [order](#order)\n         * [env and gc](#env-and-gc)\n         * [fun, lambda and λ](#lambda-and-λ)\n         * [type](#type)\n         * [macro](#macro)\n         * [macroexpand](#macroexpand)\n         * [self and environment lookups](#self-and-environment-lookups)\n         * [\u0026amp;rest](#rest)\n         * [quote](#quote)\n         * [quasiquote](#quasiquote)\n         * [unquote](#unquote)\n         * [unquote-splicing](#unquote-splicing)\n         * [eval](#eval)\n         * [apply](#apply)\n         * [gensym](#gensym)\n         * [if](#if)\n         * [cond](#cond)\n         * [loop](#loop)\n         * [begin](#begin)\n         * [try](#try)\n         * [error](#error)\n         * [print](#print)\n         * [as](#as)\n         * [list](#list)\n         * [append](#append)\n         * [range](#range)\n         * [len](#len)\n         * [string](#string)\n         * [import](#import)\n         * [assert](#assert)\n         * [exit](#exit)\n         * [debug-verbose](#debug-verbose)\n         * [math.pi and math.e](#mathpi-and-mathe)\n         * [math.floor](#mathfloor)\n         * [string.split](#stringsplit)\n      * [Standard library](#standard-library)\n         * [car, cdr, caar, cadr, cddr, caddr, last, nth](#car-cdr-caar-cadr-cddr-caddr-last-nth)\n         * [cons](#cons)\n         * [nil?](#nil-1)\n         * [atom?](#atom)\n         * [bool?](#bool)\n         * [relational functions](#relational-functions)\n         * [logical functions](#logical-functions)\n         * [let macro](#let-macro)\n         * [filter](#filter)\n         * [map](#map)\n         * [quicksort](#quicksort)\n         * [while macro](#while-macro)\n         * [list.iterate and each](#listiterate-and-each)\n         * [reduce-with](#reduce-with)\n         * [each-pair](#each-pair)\n         * [matrix functions](#matrix-functions)\n         * [hashmap](#hashmap)\n         * [io.read-number](#ioread-number)\n         * [typename](#typename-1)\n         * [time.now](#timenow)\n         * [double-quote](#double-quote)\n         * [inc! and dec!](#inc-and-dec)\n         * [file i/o and stdin/stdout](#file-io-and-stdinstdout)\n         * [math.odd? math.even? odd-items and even-items](#mathodd-matheven-odd-items-and-even-items)\n         * [math.abs](#mathabs)\n         * [math.pow](#mathpow)\n         * [math.average](#mathaverage)\n         * [math.sqrt](#mathsqrt)\n         * [math.safe-div](#mathsafe-div)\n         * [math.make-random-generator and math.random-list](#mathmake-random-generator-and-mathrandom-list)\n         * [math.fib and math.fact](#mathfib-and-mathfact)\n         * [Y](#y)\n      * [Modules](#modules)\n         * [Module example](#module-example)\n\n# Building and running\n\nClone the repository and cd to the root directory.\n\nBio currently targets Zig v13.0\n\n**Build**\n\n```bash\nzig build\n```\n\n**Run the REPL:**\n\n```bash\nzig-out/bin/bio\n```\n\n**Run a Bio source file:**\n\n```bash\nzig-out/bin run examples/triangles.lisp\n```\n\nYou can also use `import` to evaluate files from the REPL, e.g. `(import \"examples/albums.lisp\")`\n\n## Running tests\n\nThe test suite in `test.lisp` can be evaluated with `zig test src/main.zig` or `./bio run test.lisp`\n\n# Language Reference\n\nA Bio program consists of one or more s-expressions. An s-expression is recursively defined as being either\n\n1. an atom\n2. a list, which may contain lists and atoms\n\nAn atom is either a 64-bit floating point number, or a symbol. A symbol is any sequence of utf8 code points that is not a valid number. Symbols serve as strings when the symbol is enclosed in double-quotes, such as `\"This is a symbol\"`. Bio does not have a separate string type; the term string is simply used to denote a symbol serving as a string.\n\n## Naming conventions\n\n* Predicates have a question mark suffix, such as `atom?`\n* Destructive actions have an exclamation point suffix, such as `set!`\n* Sentinels are prefixed with an ampersand, such as `\u0026rest`\n* Symbols with special meaning are prefixed `#`, such as `#t`\n* Identifiers are kebab-case, while composite types such as modules are PascalCase\n\n## Intrinsics\n\nIntrinsics are built-in functions, macros, and symbols implemented by the interpreter. They are building blocks for the standard library and user programs.\n\n### nil\n\nA symbol representing the absence of a value. Note that `nil` and `'()` are considered equal.\n\n### #t and #f\n\nSymbols representing true and false, respectively. Note that `nil` coerces to `#f`.\n\n### #?\n\nA symbol representing the last top-level expression evaluation.\n\nExample:\n\n```scheme\n(+ 2 3)\n(* 2 #?)\n10\n```\n\n### #! and #value\n\nThe `#value` symbol contains the value returned by a *tried* expression. This usually removes the need to use a temporary variable when you need to both check for errors *and* use the result.\n\nThe `#!` symbol contains the error after a `try` expression. If no error occurs, this is set to `nil`.\n\n```\n(try (math.safe-div (io.read-number) (io.read-number))\n    (print \"The doubled result is: \" (* 2 #value))\n    (print \"Failed: \" #!))\n```\n\n### typename\n\nThe 'typename' functions returns a string representation of the type. The most specific classification is returned, so `(typename #t)` is \"bool\", even though #t is also a symbol.\n\n```scheme\n(typename 'a)\n\"symbol\"\n\n(typename math.pi)\n\"number\"\n\n(typename #t)\n\"bool\"\n\n(typename '(a b c))\n\"list\"\n\n(typename +)\n\"function\"\n```\n\n### number? symbol? list? bool? callable? error?\n\nPredicates to determine expression types. There's also an `atom?` predicate in the standard library.\n\nThese all return #t\n\n```scheme\n(number? -5.7)\n(number? x)\n(symbol? 'x)\n(symbol? #t)\n(bool? #t)\n(bool? #f)\n(list? '(abc))\n(callable? +)\n(error? (math.safe-div 4 0))\n```\n\n### var and define\n\n'var' creates a variable binding in the *current* environment. The binding will fail if it already exists in the current environment. The `define` function is just an alias to `var`\n\nVariable names can be any utf8 sequence that doesn't start with a number, \" and '. Variable names are case-sensitive.\n\n```scheme\n(var x 5)\n(define y 5)\n(var name (io.read-line))\n(var double (lambda (val) (* 2 val)))\n(var 😀 \"Smiley\")\n(print 😀)\n\"Smiley\"\n```\n\nLocal variables:\n\n```scheme\n(var x 2)\n(var y 2)\n(var z 2)\n(var some-function (lambda (x)\n\n    ; Allowed, y is not in the local scope\n    (var y 10)\n\n    ; Not allowed, x is a formal in the same scope\n    ; (var x 10)\n\n    (print x y z)\n))\n\n\u003e (some-function 3)\n3 10 2\n```\n\n### vars\n\nA list can be destructured into variables using `vars`\n\n\n```scheme\n(var stuff '(1 2 3))\n(vars a b c stuff)\n(assert (= a 1))\n(assert (= b 2))\n(assert (= c 3))\n```\n\n### set! and unset!\n\nChanges the value of an existing binding (values themselves are immutable). The binding is searched from current to root scope.\n\n```scheme\n; Define x, then update it\n(var x 5)\n(set! x 10)\n\n; Be evil and redefine + to mean -\n(set! + -)\n(+ 10 2)\n8\n\n; Remove binding, allowing it to be defined again\n(unset! x)\n(var x 'hey)\n```\n\nYou can optionally pass a specific environment for the binding being set as the first argument:\n\n\n```scheme\n(type Point (x y)\n    (fun area () (* x y)))\n\n(var pt (Point 5 7))\n(print \"Area:\" (pt (area)) '\\n)\n\nArea: 35\n\n; Change x by passing the pt environment to set!\n(set! pt x 6)\n(print \"Area:\" (pt (area)) '\\n)\n\nArea: 42\n```\n\n### arithmetic functions\n\nThe arithmetic functions work on floating-point numbers. Unlike infix notation, any number of arguments can be given.\n\n```scheme\n(+ -3 5)\n2\n\n(/ 2 (* 10 (+ math.pi 2 3 (- 2 3))))\n0.028004957675577865\n\n; pi symbol is same as math.pi (tip: this is available with Option+P if you're on a mac)\n(define circumference (λ (x) (* 2 π x)))\n```\n\nAdditional math functions are available in the standard library.\n\n### equality\n\nThe `=` and `~=` functions check for exact and approximate equality. The approximate case is only for numeric operands and accepts a third argument to override the tolerance (epsilon is by default 1e-7)\n\n```scheme\n(= x 5)\n(= '(1 2 3 4) nums)\n(= '(1 3) (odd-items nums))\n(~= a b 0.0005)\n```\n\nExact equality is implemented in terms of the `order` function, allowing nested lists to be compared.\n\n### order\n\nThe `order` function returns 0, -1, 1 do indicate if the first argument is equal, less than or greater than the second argument. All expression types are supported.\n\nLists are recursively compared. If list lengths differ, the shorter one is considered smaller. Empty lists and `nil` are considered equal, otherwise `nil` is always considered smaller.\n\n```scheme\n(order 100 200)\n-1\n\n(order 'def 'abc)\n1\n\n(order '(1 2 3) '(0 1 2))\n1\n\n(order '(1 2) '(0 1 2))\n-1\n\n(order 6 (* 2 3))\n0\n```\n\nStandard library functions such as `\u003c` are implemented in terms of `order`.\n\n### env and gc\n\n`env` prints the content of the current environment.\n\n```\n\u003e (env)\nEnvironment for global: Env@10db4b000\n    import = \u003cfunction\u003e, env *Env@0\n    exit = \u003cfunction\u003e, env *Env@0\n    gc = \u003cfunction\u003e, env *Env@0\n    #f = #f, env *Env@0\n    #t = #t, env *Env@0\n    #? = #t, env *Env@0\n    nil = nil, env *Env@0\n    ...\n```\n\nThe garbage collector runs periodically, though the criteria and extent are intentionally left undefined by this language reference.\n\n### fun, lambda and λ\n\nCreating functions in Bio can be done either with `fun` or `lambda`. The `fun` function is just a convenience macro that expands to a lambda definition.\n\n```scheme\n\u003e (fun square (x) (* x x))\n\u003e (square 5)\n25\n```\n\nBelow is the equivalent lambda definition:\n\n```scheme\n\u003e (var square (lambda (x) (* x x)))\n\u003e (square 5)\n25\n```\n\nDirect application without binding to a variable:\n\n```scheme\n\u003e ((lambda (x) (* x x)) 5)\n25\n```\n\nThe lambda symbol can be used in place of the lambda identifier\n\n```scheme\n(var doubler (λ (x) (* 2 x)))\n```\n\nA lambda invocation has its own environment, and the parent environment is the one that existed when the lambda was defined. In other words, Bio is lexically scoped.\n\n### type\n\nA type expression is syntax sugar for functions returning their own environment. This is useful when making composite types.\n\n```scheme\n(type Point (x y)\n    (fun area () (* x y))\n)\n\n(var pt (Point 5 7))\n(print \"Area:\" (pt (area)) '\\n)\n\n; Change x by passing the pt environment to set!\n(set! pt x 6)\n(print \"Area:\" (pt (area)) '\\n)\n```\n\nComposite types can contain local variables and other functions, just like regular lambda expression.\n\n### macro\n\nThis function creates a macro.\n\nUnlike lambdas, arguments are not evaluated when the macro is invoked. Instead, they're evaluated if and when the body does so. Note\nthat eager evaluation of macro arguments can be forced by placed `\u0026eval` in front of the formal parameter.\n \nWhen the macro is invoked, the body is evaluated. The returned expression (which represents Bio code) is then evaluated as the final result.\n\n```scheme\n(var print-with-label (macro (label \u0026rest values)\n\t`(print label \": \" ,@values)\n))\n\n(print-with-label Primes 2 3 5 7)\n\nPrimes: 2 3 5 7\n```\n\nA macro invocation has its own environment, and the parent environment is the current one. This is different from lambdas whose parent environment is the one in which the lambda was defined.\n\n### macroexpand\n\nYou can stop the evaluation of the code returned from a macro by wrapping it in `macroexpand`\n\nConsider a typical swap macro:\n\n```scheme\n(var swap (macro (a b)\n    `(let ((temp ,a))\n       (set! ,a ,b)\n       (set! ,b temp))))\n```\n\nHere's a typical usage example:\n\n```scheme\n(let ((x 3) (y 7))\n    (swap x y)\n    (print \"Swapped:\" x y \"\\n\")\n)\n\nSwapped: 7 3\n```\n\nBut now we wanna see how the macro is expanded as code instead:\n\n```scheme\n(let ((x 5) (y 8))\n    (print \"Macro expansion:\" (macroexpand (swap x y)) \"\\n\")\n)\n\nMacro expansion: (let ((temp x)) (set! x y) (set! y temp))\n```\n\nOf course, you can store away the expansion for later invocation, or just evaluate the expansion directly:\n\n```scheme\n(let ((x 5) (y 8))\n    (eval (macroexpand (swap x y)))\n    (print \"Swapped:\" x y \"\\n\")\n)\n\nSwapped: 8 5\n```\n\n### self and environment lookups\n\nThe `self` function returns the current environment as an expression. This can then be used as a function to perform lookups in that environment. This enables composite data types with their own functions, as well as modules. The distinction is purely conceptual.\n\nA top-level `(self)` call will return the root environment as an expression:\n\n```scheme\n\u003e (self)\n\u003cenv\u003e\n\n\u003e ((self) +)\n\u003cfunction\u003e\n\n\u003e ((self) (+ 1 2))\n3\n\n\u003e (((self) +) 1 2)\n3\n```\n\nAs you can see, when an environment is placed in the first position of a list, it changes which environment the following argument is looked up in.\n\nThe argument can either be a symbol, or a list which will be interpreted in the context of the new environment.\n\nUse cases of `self` include modules, composite data types, polymorphic behavior, and enabling duck-typed interfaces/protocols.\n\nSee [Modules](#modules) for more information and an example.\n\n### \u0026rest\n\nA sentinel symbol causing the rest of the arguments to be delivered as a single list argument. This enables variadic functions and macros.\n\n### quote\n\nThe `quote` function and the `'` shorthand returns the argument unevaluated.\n\n```scheme\n\u003e (quote a)\na\n\n\u003e 'a\na\n\n\u003e '(a b 1 2)\n(a b 1 2)\n\n```\n\n### quasiquote\n\nThe `quasiquote` function and the \\` shorthand returns the argument unevaluated. Unlike `quote`, however, it allows arguments to be selectively evaluated using `unquote` and `unquote-splicing`\n\n```scheme\n; Evaluate one of the list items\n\u003e (quasiquote (1 2 (unquote (+ 1 2)) 4))\n(1 2 3 4)\n\n; Same thing using shorthand notation\n\u003e `(1 2 ,(+ 1 2) 4)\n(1 2 3 4)\n\n; Use unquote-splicing to make a larger list of primes:\n\u003e (var primes '(2 3 5 7 11 13))\n\u003e `(,@primes 17 19 23)\n(2 3 5 7 11 13 17 19 23)\n```\n\nQuasi quotation is commonly used to make templates in macros, but it has uses in regular functions as well.\n\n### unquote\nIn the context of a quasiquote, evaluate the argument. The shorthand version is `,`\n\n### unquote-splicing\nIn the context of a quasiquote, evaluate the elements of the list and place the result in the enclosing list. The shorthand version is `,@`\n\n### eval\n\nEvaluates all arguments, leaving the last evaluation as the result. If quote and quasiquote expressions are encountered, these are unquoted before evaluation.\n\n```scheme\n(eval '(+ 1 2))\n3\n\n(var expr '(* 2 3))\n```\n\n### apply\n\nEvaluates the given function with the given argument list. The last argument must be a list argument. Any preceding arguments are prepended to that list. This means that `(apply + 1 '(2 3))` is equivalent to `(apply + '(1 2 3))`.\n\n```scheme\n\u003e (apply + 5 2 1 '(10 20))\n38\n\n\u003e (var list-of-numbers '(1 2 3 4))\n\u003e (apply * list-of-numbers)\n24\n\n\u003e +\n\u003cfunction\u003e\n\n\u003e (apply #? '(5 2))\n7\n```\n\nUsing `apply` is mostly useful when arguments are given as a list and the function at hand expects arguments to be passed individually.\n\n### gensym\n\nGenerates a unique symbol.\n\n```scheme\n\u003e (gensym)\ngensym_1\n\n\u003e (gensym)\ngensym_2\n```\n\n### if\n\nThe `if` expression evaluates the first argument. If true, then the second argument is evaluated, otherwise the third (optional) argument is evaluated. Each branch can have multiple expressions using constructs such as `begin` or `let`.\n\n```scheme\n(if (\u003c x 10) 'Yes 'No)\n\n(var res (if (math.odd? x) 'Odd 'Even))\n```\n\nHere's a list of if expressions from `examples/fizzbuzz-if.lisp`, none of which have an else branch:\n\n```scheme\n(if (= 0 x) (print \"Fizz\"))\n(if (= 0 y) (print \"Buzz\"))\n(if (and (!= 0 x) (!= 0 y)) (print i))\n```\n\n### cond\n\nThe `cond` expression is useful when if/else conditions lead to deep nesting. It takes a variable number of predicate/body pairs, and ends with an else clause. The else clause is *required* and consists only of the body. It must be the last entry in the cond expression.\n\nHere's the cond expression from `examples/fizzbuzz-cond.lisp`:\n\n```scheme\n(cond\n    ((and (= 0 x) (= 0 y)) (print \"FizzBuzz\" \"\\n\"))\n    ((= 0 x) (print \"Fizz\" \"\\n\"))\n    ((= 0 y) (print \"Buzz\" \"\\n\"))\n    ((print i \"\\n\"))\n)\n```\n### loop\n\nThe `loop` function loops from n to m, or until \u0026break is encountered\n\nThe current iteration is optionally available in the given induction variable.\n\n```scheme\n; loops 10 times\n(loop '(0 10) (print 'Hi\\n))\n\n; loops 10 times counting down\n(loop '(10 0) (print 'Hi\\n))\n\n; loops 10 times, current iteration count goes into the idx variable\n; the current iteration is available in the idx variable (you can call it anything)\n(loop 'idx '(0 10) (print \"Hi #\" idx \"\\n\"))\n\n; loops forever until \u0026break is encountered\n(loop 'idx '() (print \"Hi #\" idx \"\\n\") (if (= idx 4) \u0026break))\n```\n\n### begin\n\nEvaluates a list of expressions and returns the last one as the result. This is useful when more than one expression needs to be evaluated, like in the branches of the `if` function:\n\n```scheme\n(if (\u003c i 10)\n    (begin\n        (var x 5)\n        (set! x (+ x 1))\n        (print (* x 2))\n    )\n)\n\n12\n```\n\n### try\n\nThe `try` function evaluates the first argument. If the result is not an error expression, then the second argument is evaluated (the success branch), otherwise the third argument is evaluated (the error branch). The error branch is optional (in which case `nil` will be returned; add an error branch if you want to propagate the error.)\n\n\nIt's often necessary to know the value of the tried expression if it succeeds. This can be done using an intermediary variable, or by looking up the `#value` symbol:\n\n```scheme\n(try (math.safe-div 6 2)\n    (if (= #value 3)\n        (print \"As expected!\\n\")\n    )\n)\n```\n\nThe error expression is available through the `#!` symbol, used here by the error branch:\n\n```scheme\n(try (math.safe-div 6 2)\n    (if (= #value 3)\n        (print \"As expected!\\n\")\n    )\n    (print \"Could not divide: \" #!)\n)\n```\n\nA function that may fail does not have to be used in a try function:\n\n```scheme\n\u003e (var res (math.safe-div x y))\n3.5\n\n\u003e (math.safe-div x 0)\nDivision by zero\n```\n\nYou can also use `try` after the fact:\n\n```scheme\n\u003e (var res (math.safe-div x 0))\n\u003e (try (math.safe-div 1 0) #t `(string \"Not good: \" ,#!))\nNot good: Division by zero\n```\n\n### error\n\nThe `error` function creates a new error expression.\n\n```scheme\n(var fail-if-ten (lambda (x)\n    (if (= x 10)\n        (error \"10 is not allowed\")\n        #t\n    )\n))\n\n(try (fail-if-ten 10)\n    \"All good\"\n    (begin\n        (print \"Something went terribly wrong!\\n\")\n        #!\n    )\n)\n\nSomething went terribly wrong!\n10 is not allowed\n```\n\nNotice how the `try` expression propagates the error by putting `#!` as the last expression in the error case.\n\nErrors don't have to be symbols, any expression will do.\n\n### print\n\n`print` prints one or more expressions separated by a space. Combine `print` with `string` if you need to print verbatim (without spaces between expressions)\n\n```scheme\n(print \"What's your name?\")\n(var name (io.read-line))\n\n(print \"What's your age?\")\n(var age (as number (io.read-line)))\n\n(print \"Hi\" name (if (\u003e age 80) \"... you're quite old\" \"\"))\n```\n\nIO examples work best if you put them in a file and then use `(import \"thename.lisp\")` or `bio run thename.lisp`\n\nSee also the `io.` functions in the standard library.\n\n### as\n\nThe `as` function converts an expression from one type to another. If a conversion is not supported, `nil` is returned.\n\nThe target conversion is either `number`, `symbol`, or `list`.\n\n```scheme\n(var age (as number (io.read-line)))\n(assert (= '(5) (as list 5)))\n(as symbol mynumber)\n(set! age (as symbol age))\n```\n\n### list\n\nCreates a new list from its arguments. Quoting can be used to create lists without evaluating the expressions.\n\n```scheme\n\u003e (var x 3)\n\u003e (list 1 2 x)\n(1 2 3)\n\n\u003e '(1 2 x)\n(1 2 x)\n```\n### append\n\nCreates a new list from its arguments. List arguments are spliced into the new list.\n\n```scheme\n\u003e (append '(a b c) '(d e f (g h)))\n(a b c d e f (g h))\n\n\u003e (append 'a 'b 1 2 (+ 1 2))\n(a b 1 2 3)\n```\n\n### range\n\nThe `range` function is the building block for querying lists, and is used to build standard library functions such as `car` and `cdr`.\n\n* If called with no arguments, the first expression in a list is returned.\n* If called with one argument, a list containing the sublist from `start` to end-of-list is returned.\n* If called with two arguments, a list containing the sublist from `start` to `end` (exclusive) is returned.\n\nNegative indices are end-of-list relative. `nil` is returned if any indices are out of range.\n\n```scheme\n\u003e (var letters '(a b c d e))\n(a b c d e)\n\n\u003e (range letters)\na\n\n\u003e (range letters -1)\n(e)\n\n\u003e (range (range letters -1))\ne\n\n\u003e (range letters 2)\n(c d e)\n\n\u003e (range letters 2 4)\n(c d)\n\n\u003e (range letters -4 -2)\n(c d)\n\n```\n\n### len\n\nThe length of a list (in item count) or symbol (in bytes)\n\n```scheme\n\u003e (len '(1 2 3))\n3\n```\n\n### string\n\nCreates a symbol by concatenating the *rendering* of its arguments.\n\n```scheme\n\u003e (var message (string \"The value is \" x))\nThe value is 5\n\n\u003e (string \"An error occurred : \" #!)\nAn error occurred : Division by zero\n```\n\n### import\n\nReads and evaluates the given file. The path can be relative or absolute.\n\n```scheme\n(import \"examples/albums.lisp\")\n```\n\n### assert\n\nChecks if the expression evaluates to #t. If not, an error is printed and the process is terminated. Evaluates to #t if successful.\n\n```scheme\n(assert (= '(a b c 1 2 3) mylist))\n```\n\n### exit\n\nExits the process with an optional exit code (default is 0)\n\n```scheme\n(exit 1)\n```\n\n### debug-verbose\n\nToggles the verbosity flag. When on, some details are printed during evaluation, such as `nil` results and quasiquote expansions.\n\n### Math pi and e\n\nThe values of π and Euler's number respectively. The symbol `π` is an alias to `pi` in the Math module.\n\n### Math floor\n\nReturns the largest integer less than or equal to the argument.\n\n```scheme\n(var math (Math))\n(math (floor (math pi)))\n3\n```\n\n### string.split\n\nGiven one or more delimiters, tokenizes the input symbol and produces a list of symbols.\n\n```scheme\n(assert (= (string.split \"\" \",\") '()))\n(assert (= (string.split \"a\" \",\") '(a)))\n(assert (= (string.split \"a,b,c\" \",\") '(a b c)))\n(assert (= (string.split \"a,b,c,\" \",\") '(a b c)))\n(assert (= (string.split \"a,b;c,\" \",;\") '(a b c)))\n(assert (= (string.split \" \" \",\") (list \" \")))\n```\n\n## Standard library\n\nThe standard library is a file called `std.lisp` that's loaded and evaluated when the interpreter starts.\n\n### car, cdr, caar, cadr, cddr, caddr, last, nth\n\nThese functions treat a list as pairs in the classical Lisp sense. `car` returns the first list item, while `cdr` returns the rest of the list. `last` returns the last list item.\n\n```scheme\n(var nums '(1 2 3 4))\n(assert (= 1 (car nums)))\n(assert (= 2 (cadr nums)))\n(assert (= 3 (caddr nums)))\n(assert (= 4 (last nums)))\n(assert (= 3 (nth 2 nums)))\n(assert (nil? (nth 100 nums)))\n\n```\n\n### cons\n\nPrepends an item to a list:\n\n```scheme\n\u003e (cons 'a '(b c))\n(a b c)\n\n\n```\n\n### nil?\n\nTrue if the argument is `nil` or an empty list.\n\n### atom?\n\nTrue if the argument is a number or a symbol (in other words, not a list)\n\n### bool?\n\nTrue if the argument is `#t` or `#f`\n\n### relational functions\n\nThe relation functions are `\u003c=`, `\u003c`, `\u003e`, `\u003e=`, `!=` in addition to the intrinsic `=`\n\n### logical functions\n\nThe `and` and `or` macros perform the usual shortcut evaluation. The `not` function checks if the argument is false. If so, the result is then `#t`, otherwise it's `#f`\n\n### let macro\n\nLocal bindings can be created with a `lambda` expression. If the only reason to create a lambda is to have local variables, then the `let` macro is more convenient.\n\n```scheme\n(var x 10)\n(var y 20)\n\n(let ((x 5) (y 6))\n    (+ x y)\n)\n\n11\n```\n\n### filter\n\nFilters a list.\n\n```scheme\n\u003e (filter (lambda (x) (\u003c x 5)) '(3 9 5 8 2 4 7))\n(3 2 4)\n```\n\n### map\n\nApplies a function over one or more lists.\n\n```scheme\n; Create a list of sums taking operands from three lists\n\u003e (map + '(0 2 5) '(1 2 3) '(1 2 3))\n(2 6 11)\n\n; Double every element in a list\n\u003e (map (λ (x) (* 2 x)) '(1 2 3))\n(2 4 6)\n\n; A list of pairs with the order reversed\n\u003e (map (λ (x y) (list y x)) '(a b c) '(1 2 3))\n((1 a) (2 b) (3 c))\n```\n\n### quicksort\n\nSorts a list using the supplied comparator function. The following example sorts the same list in ascending and descending order by passing `\u003c` and `\u003e` as the comparator functions. In the ascending example, we also filter out negative numbers:\n\n```scheme\n\u003e (filter\n    (quicksort '(5 40 1 -3 2) \u003c)\n    (λ (x) (\u003e= x 0)))\n(1 2 5 40)\n\n\u003e (quicksort '(5 40 1 -3 2) \u003e)\n(40 5 2 1 -3)\n\n```\n\nYou can also pass a lambda to do your own ordering. See the **albums** example file for an example of doing this to sort albums.\n\n### while macro\n\nExpands to a tail-recursive function running a body while the predicate holds:\n\n```scheme\n(while (\u003c c 10000000)\n    (print \"Value is now \" c \"\\n\")\n    (inc! c)\n)\n```\n\n### list.iterate and each\n\nThe `list.iterate` function allows for convenient iteration of lists. The first argument is a list. The second argument is a function that's called for every item in the list, with the item as an argument. `each` is an alias to this function.\n\n```scheme\n; Print all numbers\n(each lots-of-numbers print)\n\n; Create a new list of numbers, with double the values\n(var result '())\n(each nums (lambda (item)\n    (set! result (append result (* 2 item)))\n))\n\n(2 4 6 8 10 12)\n```\n\n### reduce-with\n\nSignature: `(reduce-with initial fn op list)`\n\nCalls `op` on every item in `list`, but only after applying the function `fn` to the item.\n\n```scheme\n(reduce-with 0 (lambda (x) (+ x 1)) + '(1 2 3))\n\n9\n```\n\nThis works like this:\n\n* start with the list `'(1 2 3)`\n* apply the lambda which adds 1 to each element, leaving `'(2 3 4)`\n* reduce to a single number `9` using `+` with the initial number `0`\n\n### each-pair\n\nCalls a supplied lambda with each consecutive pair in a list.\n\n```scheme\n; Pair iteration where each pair 1 5, 3 3 and 4 2 all sum to 6\n(each-pair '(1 5 3 3 4 2)\n    (λ (a b)\n        (assert (= 6 (+ a b)))))\n```\n\n### matrix functions\n\n```scheme\n; Multidimensional list (matrix) access\n(var M '(((10 11 12) (13 14 15)) ((16 17 18) (19 20 21))))\n(assert (= 20 (matrix-at M 1 1 1)))\n(assert (= nil (matrix-at M 1 1 100)))\n(assert (= '(10 11 12) (matrix-at M 0 0)))\n\n(var M2 '( (1 2 3 4) (a b c d)))\n(assert (= 'c (matrix-at M2 1 2)))\n\n; Update matrix; the old value is returned\n(assert (= 'c (matrix-set! M2 'x 1 2)))\n(assert (= 'x (matrix-at M2 1 2)))\n\n; Trying to set a value outside the matrix returns an error\n(assert (error? (try (matrix-set! M2 0 200 2) #t #!)))\n```\n\n### hashmap\n\nThe are numerous hashmap related functions available:\n\n```scheme\n(var mymap (hashmap.new (\"1\" 2) (3 4)))\n(assert (hashmap? mymap))\n(assert (= (len mymap) 2))\n(hashmap.put mymap 5 6)\n(hashmap.put mymap 7 \"Initial entry\")\n(var initial-entry (hashmap.put mymap 7 \"Another entry\"))\n(assert (= initial-entry \"Initial entry\"))\n(assert (= (len mymap) 4))\n(assert (= (hashmap.get mymap 7) \"Another entry\"))\n(hashmap.remove mymap 7)\n(assert (= (hashmap.get mymap 7) nil))\n(assert (= (len mymap) 3))\n\n(var keys '())\n(var vals '())\n(hashmap.iterate mymap (λ (k v)\n    (item-append! keys k)\n    (item-append! vals v)\n))\n(assert (= '(1 3 5) keys))\n(assert (= '(2 4 6) vals))\n\n(var count-removed (hashmap.clear mymap))\n(assert (= count-removed 3))\n(assert (= (len mymap) 0))\n\n; k -\u003e '()\n(var hmlist (hashmap.new))\n(hashmap.append! hmlist 'a 1)\n(hashmap.append! hmlist 'a 2)\n(assert (= (hashmap.get hmlist 'a) '(1 2)))\n\n; The 'a entry exists, so the lambda is called, which updates the list\n(hashmap.put-or-apply hmlist 'a 3 (λ (list)\n    (assert (= list '(1 2)))\n    (item-append! list 3)\n))\n\n(assert (= (hashmap.get hmlist 'a) '(1 2 3)))\n(assert (= (hashmap.maybe-put hmlist 'a '(5 5 5)) '(1 2 3)))\n(assert (= (hashmap.maybe-put hmlist 'b '(5 5 5)) '(5 5 5)))\n\n(assert (contains? hmlist 'a))\n(assert (not (contains? hmlist 'not-there)))\n```\n\n### io.read-number\n\nRead a number from stdin.\n\nIf input is not a number, an `error` expression is returned.\n\n### typename\n\nReturns the type name of its argument\n\n```scheme\n\u003e(define x 5)\n\u003e(typename x)\nnumber\n\n\u003e(typename 'x)\nsymbol\n```\n\n### time.now\n\nReturns the current time in milliseconds since unix epoch:\n\n```scheme\n\u003e(time.now)\n1618413357184\n```\n\n### double-quote\n\nRenders the argument and wraps the result in double-quotes.\n\n```scheme\n\u003e (double-quote name)\n\"Joanna\"\n\n\u003e (double-quote 5)\n\"5\"\n\n\u003e (double-quote (+ 2 3))\n\"5\"\n```\n\n### inc! and dec!\n\nIncrements and decrements\n\n### file i/o and stdin/stdout\n\nA file is opened with `io.open-file`, which takes a relative or absolute path name as an argument. The file is then closed with `io.close-file`. Currently, line oriented reading and writing is supported.\n\n```scheme\n(var report (io.open-file \"report.csv\"))\n(io.read-line report)\n(io.write-line report \"a new line is appended\")\n(io.close-file report)\n```\n\n`io.read-line` returns the *error* expression \"EOF\" if end of the file is reached.\n\n`io.read-line` and `io.write-line` without a file argument reads and writes to stdin and stdout respectively.\n\n`io.read-byte` reads one byte at a time from a file.\n\n### math.odd? math.even? odd-items and even-items\n\n`math.odd?` and `math.even?` determine if a number is odd or even, respectively.\n\n`odd-items` and `even-items` create a list with odd- and even indexed items respectively. These functions are 1 based.\n\n```scheme\n\u003e(math.odd? 5)\n#t\n\n\u003e (odd-items '(a b c d e f g))\n(a c e g)\n```\n\n### math.abs\n\nReturns the absolute value of the argument.\n\n```scheme\n(math.abs -17)\n17\n```\n\n### math.pow\n\nCalculates `x^y`:\n\n```scheme\n\u003e(math.pow 2 32)\n4294967296\n```\n\n### math.average\n\nThe average of a list of numbers\n\n### math.sqrt\n\nCalculate the square root using Newton's method\n\n```scheme\n\u003e(math.sqrt 986)\n31.40063693621559\n```\n\n### math.safe-div\n\nDivides the first argument by the second. If the divisor is zero, the result is an error.\n\n```scheme\n(try (safe-div 6 2)\n    (if (= #value 3)\n        (print \"As expected!\\n\")\n    )\n)\n```\n\n### math.make-random-generator and math.random-list\n\nGiven a seed, `math.make-random-generator` creates a linear congruent random number generator.\n\nExample using current Unix epoch as seed:\n\n```scheme\n\u003e(var rng (math.make-random-generator 0))\n\n\u003e(rng)\n362807296\n\n\u003e(rng)\n1965043776\n```\n\nGiven a random number generator, `math.random-list` generates a list of `n` random numbers:\n\n```scheme\n\u003e(var rng (math.make-random-generator 0))\n\n\u003e(math.random-list rng 10)\n(1055406848 752570112 91411200 3016512 1968096889 765038592 339902464 1666232384 1888402176 197119744)\n```\n\n### math.fib and math.fact\n\nRecursive Fibonacci and factorial functions (note: as Bio doesn't support arbitrary precision numbers yet, it can only handle relatively small inputs)\n\n### Y\n\nThe Y fixpoint combinator (technically, the Z combinator as Bio is applicative)\n\n```scheme\n\u003e (var ! (Y (lambda (r) (lambda (x) (if (\u003c x 2) 1 (* x (r (- x 1))))))))\n\u003e (! 5)\n\n120\n```\n\n## Modules\nA Bio module is a module *by convention*, somewhat similar to classical Javascript modules:\n\n1. A `mod-\u003cmodulename\u003e.lisp` file with the contents wrapped in a lambda call\n2. The last expression is `(self)`, making the environment available to the importer\n3. The following definitions are available:\n    - module-name, a string describing the module\n    - module-version, a list of numbers signifying major, major, and patch\n    - module-description, an *optional* description of the module\n\n## Modules\nA Bio module is a module *by convention*, somewhat similar to classical Javascript modules:\n\n1. A `mod-\u003cmodulename\u003e.lisp` file with the contents wrapped in a lambda call\n2. The last expression is `(self)`, making the environment available to the importer\n3. The following definitions are available:\n    - module-name, a string describing the module\n    - module-version, a list of numbers signifying major, major, and patch\n    - module-description, an *optional* description of the module\n\n### Module example\n\nThe examples directory contains a sample module called `mod-pos.lisp`\n\nTo use the module in a REPL:\n\n```scheme\nbio\u003e  (var Point (import \"examples/mod-pos.lisp\"))\n\u003cenv\u003e\n\nbio\u003e  (var pt (Point (new-point 2 5.4)))\n\u003cenv\u003e\n\nbio\u003e (pt x)\n2\n\nbio\u003e (pt y)\n5.4\n\nbio\u003e (pt (as-string))\n2 5.4\n\nbio\u003e (var loc (Point (new-location 100.5 200.5)))\n\u003cenv\u003e\n\nbio\u003e (loc x)\n100.5\n\nbio\u003e (loc y)\n200.5\n\nbio\u003e (loc (as-string))\n100.5° N  200.5° E\n\n```\n","funding_links":[],"categories":["Zig"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcryptocode%2Fbio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcryptocode%2Fbio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcryptocode%2Fbio/lists"}