{"id":13995119,"url":"https://github.com/NoahGav/oxide-lang","last_synced_at":"2025-07-22T21:32:08.285Z","repository":{"id":204605841,"uuid":"712191536","full_name":"NoahGav/oxide-lang","owner":"NoahGav","description":null,"archived":true,"fork":false,"pushed_at":"2024-03-19T01:38:55.000Z","size":310,"stargazers_count":58,"open_issues_count":6,"forks_count":1,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-08-10T14:18:23.673Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NoahGav.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2023-10-31T01:22:34.000Z","updated_at":"2024-05-10T10:51:56.000Z","dependencies_parsed_at":"2024-01-20T17:58:26.642Z","dependency_job_id":"c378842e-5954-4b07-9f13-55bc0d5058fb","html_url":"https://github.com/NoahGav/oxide-lang","commit_stats":null,"previous_names":["noahgav/oxide-lang"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NoahGav%2Foxide-lang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NoahGav%2Foxide-lang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NoahGav%2Foxide-lang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NoahGav%2Foxide-lang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NoahGav","download_url":"https://codeload.github.com/NoahGav/oxide-lang/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227177783,"owners_count":17743167,"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":[],"created_at":"2024-08-09T14:03:15.577Z","updated_at":"2024-11-29T17:31:04.271Z","avatar_url":"https://github.com/NoahGav.png","language":"Rust","readme":"# Archival\n\nAfter careful consideration, I have decided that this project is not worth pursuing, at\nleast for the foreseeable future. As a result, it will be archived.\n\nThank you for your interest and contributions thus far.\n\n# Prototype\n\n### Branch\n\nThe main development branch is the\n[prototype](https://github.com/NoahGav/oxide-lang/tree/prototype) branch.\n\n### Contributions\n\nTo contribute:\n\n1. [Fork](https://github.com/NoahGav/oxide-lang/fork) the repo.\n2. Checkout the\n   [prototype branch](https://github.com/NoahGav/oxide-lang/tree/prototype)\n   (`git checkout prototype`).\n3. Make your contributions.\n4. Then submit a [pull request](https://github.com/NoahGav/oxide-lang/pulls)\n   when you are finished.\n\n### Usage\n\nTo use the Oxide binaries:\n\n1. run `cargo build --release`.\n2. Add `target/release` to the `Path` environment variable (on Windows).\n3. Open a terminal and run a command (e.g. `oxide analyzer`).\n\nTo use (and develop) the vscode extension:\n\n1. Open `editors/vscode` in it's own vscode window.\n2. Start debugging the extension by pressing `F5`.\n3. A new vscode window will open. You should open an Oxide project in that window.\n\n# Oxide Proposal\n\n### Your Feedback Matters\n\nOxide is a personal project that takes inspiration from the principles discussed\nin\n[\"Notes on a Smaller Rust\"](https://boats.gitlab.io/blog/post/notes-on-a-smaller-rust/)\nand its follow-up,\n[\"Revisiting a 'smaller Rust'\"](https://without.boats/blog/revisiting-a-smaller-rust/).\nIt aims to explore a new language design that simplifies and optimizes the\ndevelopment process while inheriting Rust's best qualities.\n\nI'm excited to share this early Oxide specification with you and seek your\nthoughts, criticisms, and feedback. Your input is invaluable as it will help\nshape the future of Oxide. Whether you're interested in contributing to the\ndevelopment of the language, have suggestions, or simply want to express your\nthoughts, I welcome your participation.\n\n### Goal\n\nFeel free to explore the specification and let me know your insights. Your\nfeedback will play a vital role in determining whether Oxide should progress to\na fully-fledged language development project (or stay just an idea).\n\n- [1. Implicit Lifetime Handling in Oxide](#1-implicit-lifetime-handling-in-oxide)\n  - [1.1 Eliminating Explicit Lifetime Annotations](#11-eliminating-explicit-lifetime-annotations)\n  - [1.2 Ban on Returning References from Functions](#12-ban-on-returning-references-from-functions)\n  - [1.3 Optimized for Application Development](#13-optimized-for-application-development)\n- [2. Implicit Trait Implementation with Attribute Support](#2-implicit-trait-implementation-with-attribute-support)\n  - [2.1 Implicit Trait Implementation](#21-implicit-trait-implementation)\n  - [2.2 Attribute Support for Trait Implementation](#22-attribute-support-for-trait-implementation)\n- [3. Unified Approach to Data Allocation](#3-unified-approach-to-data-allocation)\n  - [3.1 Stack and Heap Unification](#31-stack-and-heap-unification)\n- [4. Powerful Macros in Oxide](#4-powerful-macros-in-oxide)\n  - [4.1 Macros as Regular Functions](#41-macros-as-regular-functions)\n  - [4.2 @derive(Trait, ...) Macro](#42--derive-trait----macro)\n  - [4.3 Incremental Computation](#43-incremental-computation)\n- [5. Algebraic Types (Tagged Unions)](#5-algebraic-types--tagged-unions-)\n  - [5.1 Defining Algebraic Types](#51-defining-algebraic-types)\n  - [5.2 Pattern Matching](#52-pattern-matching)\n  - [5.3 Use Cases for Algebraic Types](#53-use-cases-for-algebraic-types)\n  - [5.4 Benefits of Algebraic Types](#54-benefits-of-algebraic-types)\n- [6. Error Handling in Oxide](#6-error-handling-in-oxide)\n  - [Introduction](#introduction)\n    - [6.1 Result Type (T?)](#61-result-type--t--)\n    - [6.2 The ? Operator](#62-the---operator)\n    - [6.3 @bail Macro](#63--bail-macro)\n    - [6.4 Custom Error Types](#64-custom-error-types)\n    - [6.5 Explicit Error Handling](#65-explicit-error-handling)\n    - [6.6 try { ... } Blocks](#66-try------blocks)\n      - [Note](#note)\n    - [6.7 Pattern Matching on the Error Trait](#67-pattern-matching-on-the-error-trait)\n- [7. The `??` Operator](#7-the------operator)\n  - [7.1 Usage](#71-usage)\n  - [7.2 Benefits](#72-benefits)\n  - [7.3 Examples](#73-examples)\n  - [Conclusion](#conclusion)\n- [8. The `=\u003e` Syntax in Oxide](#8-the------syntax-in-oxide)\n  - [Introduction](#introduction-1)\n    - [8.1 Single Expression Statements](#81-single-expression-statements)\n    - [8.2 Function Definitions](#82-function-definitions)\n    - [8.3 Conditional Statements](#83-conditional-statements)\n    - [8.4 Error Handling Precision](#84-error-handling-precision)\n    - [Conclusion](#conclusion-1)\n- [9. Garbage-Collecting Shared References in Oxide](#9-garbage-collecting-shared-references-in-oxide)\n  - [9.1 The `Gc\u003cT\u003e` Type](#91-the--gc-t---type)\n  - [9.2 Automatic Interior Mutability](#92-automatic-interior-mutability)\n    - [Note](#note-1)\n  - [9.3 Cyclic Garbage Collection in `Gc\u003cT\u003e`](#93-cyclic-garbage-collection-in--gc-t--)\n    - [9.3.1 Reference Counting](#931-reference-counting)\n    - [9.3.2 Cyclic Garbage Collection](#932-cyclic-garbage-collection)\n  - [9.4 Using `Gc\u003cT\u003e` in Oxide](#94-using--gc-t---in-oxide)\n  - [9.5 Implementation of the Copy Trait](#95-implementation-of-the-copy-trait)\n  - [9.6 Example](#96-example)\n- [10. The Copy Trait in Oxide](#10-the-copy-trait-in-oxide)\n  - [10.1 Implicit Cloning on Move](#101-implicit-cloning-on-move)\n  - [10.2 Interactions with the Clone Trait](#102-interactions-with-the-clone-trait)\n  - [10.3 Simplified Data Sharing](#103-simplified-data-sharing)\n- [11. Examples](#11-examples)\n  - [11.1 Error Handling](#111-error-handling)\n- [12. IDE and Tooling Support](#12-ide-and-tooling-support)\n  - [12.1 The Oxide Compiler API](#121-the-oxide-compiler-api)\n    - [**Incremental Compilation**](#--incremental-compilation--)\n    - [**Concurrency**](#--concurrency--)\n    - [**IDE Integration**](#--ide-integration--)\n  - [12.2 Compiler Plugins](#122-compiler-plugins)\n  - [12.3 Versatile Tooling](#123-versatile-tooling)\n- [13. Final Thoughts](#13-final-thoughts)\n  - [13.1 Structs in Oxide](#131-structs-in-oxide)\n  - [13.2 Concurrency](#132-concurrency)\n  - [13.3 Macro Code Generation](#133-macro-code-generation)\n  - [13.4 Namespaces](#134-namespaces)\n  - [13.5 String Interpolation](#135-string-interpolation)\n  - [13.6 Arithmetic](#136-arithmetic)\n    - [Note](#note-2)\n  - [13.7 Oxide Scripting Language](#137-oxide-scripting-language)\n\n# 1. Implicit Lifetime Handling in Oxide\n\nOxide's design philosophy centers around simplicity and efficiency without\nsacrificing safety. One key aspect of this approach is its implicit lifetime\nhandling. Oxide borrows Rust's borrow checking mechanism but dispenses with the\nneed for explicit lifetime annotations, making it more accessible to developers.\n\n### 1.1 Eliminating Explicit Lifetime Annotations\n\nIn Oxide, the handling of lifetimes is simplified by eliding all lifetimes, even\nwithin structs. This approach significantly simplifies lifetime management,\neliminating the need for explicit annotations. While Rust theoretically supports\nelided lifetimes, Oxide embraces this feature more explicitly from the ground\nup. This means that lifetimes are automatically inferred and validated by the\ncompiler, reducing the cognitive burden on developers.\n\n### 1.2 Ban on Returning References from Functions\n\nTo maintain clarity and simplicity while avoiding the complexity of explicit\nlifetimes, Oxide introduces a restriction on returning references from\nfunctions. While the Rust language encounters challenges when multiple `\u0026`\nreferences of the same type are passed to a function, Oxide simplifies this\nscenario by disallowing functions to return references. For instance, in Rust, a\nfunction like:\n\n```rust\n// This code gives a compile-time error as rust cannot\n// know if the lifetime returned is from a, b, or c.\nfn foo\u003cT\u003e(a: \u0026T, b: \u0026T, c: \u0026T) -\u003e \u0026T { ... }\n\n// However, the following code has no errors as rust\n// ensures the lifetime returned is the same as the\n// \u0026self lifetime. This is why oxide prohibits returning\n// references from functions, but not methods.\nstruct Foo;\n\nimpl Foo {\n    fn foo\u003cT\u003e(\u0026self, a: \u0026T, b: \u0026T, c: \u0026T) -\u003e \u0026T {\n        todo!()\n    }\n}\n```\n\nPresents challenges for the compiler to determine which reference's lifetime\nshould be returned. Oxide circumvents this ambiguity by enforcing a ban on\nreturning references from functions. However, references can still be returned\nin methods where the lifetime of the returned reference is guaranteed to be at\nleast the same as the `\u0026self` reference. This approach optimizes Oxide for\napplication development and simplifies the codebase without sacrificing safety.\n\n### 1.3 Optimized for Application Development\n\nOxide's approach to implicit lifetime handling is tailored for application\ndevelopment, offering the benefits of Rust's robust safety features while\nstreamlining the coding process. This design choice optimizes Oxide for\napplication development tasks, ensuring that developers can build reliable and\nefficient software with ease. This simplified lifetime management not only\nenhances code readability but also reduces the need for extensive error\nchecking, making development in Oxide more intuitive and productive.\n\n# 2. Implicit Trait Implementation with Attribute Support\n\n### 2.1 Implicit Trait Implementation\n\nIn Oxide, trait implementation is primarily implicit. When a type defines all\nthe methods required by a specific trait, it automatically implements that\ntrait. This approach draws inspiration from Go's interfaces, offering a\nsimplified way to define and use traits without the need for explicit\ndeclarations.\n\n### 2.2 Attribute Support for Trait Implementation\n\nOxide introduces the @impl(Trait); attribute to address scenarios where multiple\ntraits may have methods with interfering signatures. This attribute allows\ndevelopers to specify the trait to which a method implementation belongs,\nresolving potential conflicts and ensuring precise trait behavior for each trait\na type implements. For example,\n\n```rust\ntype Foo;\n\n@impl(Display);\nfn Foo.fmt(\u0026self, f: \u0026mut Formatter) -\u003e ? { ... }\n\n@impl(Debug);\nfn Foo.fmt(\u0026self, f: \u0026mut Formatter) -\u003e ? { ... }\n```\n\n# 3. Unified Approach to Data Allocation\n\n### 3.1 Stack and Heap Unification\n\nOxide eliminates the distinction between stack and heap allocation, offering a\nmore unified and straightforward approach to data allocation. The compiler\nautomatically determines the appropriate allocation strategy based on the\nruntime size of data, reducing developer overhead and complexity. This unified\napproach simplifies working with data in Oxide and streamlines the language's\nmemory management.\n\n# 4. Powerful Macros in Oxide\n\nOxide features a sophisticated and powerful macro system that provides\ncomprehensive compile-time reflection and code generation capabilities. Unlike\ntraditional macros in many languages, Oxide's macros are defined as regular\nfunctions that take in compile-time reflection objects, empowering developers\nwith an enhanced level of control and expressiveness.\n\n### 4.1 Macros as Regular Functions\n\nIn Oxide, macros are designed to resemble regular functions, simplifying their\nusage and making them more approachable. Rather than relying on token streams,\nmacros take advantage of compile-time reflection objects, such as the `Type`\nobject. This compilation-time data holds complete semantic information about the\nentire program, enabling tailored implementations that are both powerful and\nprecise.\n\n### 4.2 @derive(Trait, ...) Macro\n\nOne of the standout examples of Oxide's macro capabilities is the\n`@derive(Trait, ...)` macro. This macro is responsible for generating\nimplementations of a specified trait for a given type. By leveraging the `Type`\nobject, the `@derive` macro creates `Method` objects, representing the\nimplementations of the trait for the type. This approach provides a level of\ndetail and customization that is not possible with simplistic token streams.\n\n### 4.3 Incremental Computation\n\nOxide's macro system incorporates a concept known as incremental computation.\nWhen the compiler processes a program, it first builds a semantic model without\nmacros. Subsequently, macros are executed to generate code, and the semantic\nmodel is reconstructed. Thanks to this incremental computation, the reevaluation\nof the semantic model becomes highly efficient, especially when source files\nremain unchanged.\n\nOxide's approach to macros offers greater flexibility and precision, enabling\ndevelopers to create custom code generators and extensions with ease. The use of\ncompile-time reflection objects enhances the robustness of macros and simplifies\nthe code generation process, making it a standout feature of the language.\n\n# 5. Algebraic Types (Tagged Unions)\n\nIn Oxide, algebraic types, commonly referred to as tagged unions, provide a\npowerful and flexible mechanism for defining complex data structures that can\nhave multiple shapes or variants. These types, although conceptually similar to\nRust's enums, are designed with the aim of enhancing code expressiveness and\nsimplifying data modeling.\n\n### 5.1 Defining Algebraic Types\n\nDefining algebraic types in Oxide is straightforward, allowing you to declare a\ntype with multiple variants using a syntax that is reminiscent of Rust. The\nsyntax provides an intuitive way to specify these variants and their associated\ndata. Algebraic types can be declared as follows:\n\n```rust\ntype EnumName =\n    Variant1 |\n    Variant2 |\n    Variant3(Foo) |\n    Variant4(bar: Bar);\n```\n\n- `EnumName` is the name of the algebraic type.\n- `Variant1`, `Variant2`, `Variant3`, and `Variant4` are the possible variants\n  of the type.\n- Variants can include associated data, like `Variant3(Foo)` and\n  `Variant4(bar: Bar)`. This allows you to attach additional information to a\n  variant when necessary.\n\n### 5.2 Pattern Matching\n\nPattern matching on algebraic types in Oxide is nearly identical to Rust,\noffering a familiar and powerful way to handle different variants. Pattern\nmatching allows developers to write code that responds to the shape of data,\nmaking it an essential tool for data processing and control flow.\n\nHere is an example of pattern matching in Oxide:\n\n```rust\nfn process_enum(enum_val: EnumName) =\u003e match enum_val {\n    Variant1 =\u003e {\n        // Handle Variant1\n    }\n    Variant2 =\u003e {\n        // Handle Variant2\n    }\n    Variant3(foo) =\u003e {\n        // Handle Variant3 with associated data `foo`\n    }\n    Variant4 { bar } =\u003e {\n        // Handle Variant4 with associated data `bar`\n    }\n};\n```\n\nPattern matching enables developers to inspect and process data based on its\nvariant, making it a powerful tool for working with algebraic types.\n\n### 5.3 Use Cases for Algebraic Types\n\nAlgebraic types are particularly valuable when modeling data with different\nshapes or behaviors. They can represent a variety of scenarios, such as:\n\n- Representing different states of an application or system.\n\n- Modeling data with variable structures, such as nodes in a tree or items in a\n  document.\n\n- Defining error types with different error codes and associated data.\n\n- Handling complex parsing or transformation tasks by distinguishing between\n  various forms or structures.\n\nBy allowing for clear and concise modeling of these scenarios, algebraic types\ncontribute to improved code readability and maintainability in Oxide. They are a\nfundamental feature for creating data structures that can adapt to different\nsituations in a clean and expressive manner.\n\n### 5.4 Benefits of Algebraic Types\n\nThe use of algebraic types in Oxide offers several benefits:\n\n- **Code Clarity**: Algebraic types make the code more self-explanatory by\n  clearly defining the possible shapes of data.\n\n- **Type Safety**: Pattern matching ensures that you handle all possible\n  variants, reducing the risk of unexpected behaviors.\n\n- **Flexible Data Modeling**: They allow you to create data structures that can\n  evolve with changing requirements without extensive code modifications.\n\n- **Error Handling**: Algebraic types provide an elegant way to model and handle\n  errors, making error management more structured and predictable.\n\n- **Readable and Maintainable Code**: By modeling data with algebraic types, you\n  create code that is easier to understand, modify, and maintain.\n\nAlgebraic types, as tagged unions, contribute to Oxide's aim of providing a\nreliable and efficient language while maintaining simplicity and expressiveness\nin code development.\n\n# 6. Error Handling in Oxide\n\n## Introduction\n\nError handling is a fundamental aspect of software development, and Oxide\nstrives to provide a simple, yet robust and powerful approach to error\nmanagement. This formal specification outlines the various techniques and\nfeatures Oxide offers to handle errors effectively while ensuring code safety\nand reliability.\n\n### 6.1 Result Type (T?)\n\nIn Oxide, error handling begins with the use of the T? type, which signifies the\npotential for errors. Functions that may produce errors return a T?, where T is\nthe result type. This standardized approach ensures clear and explicit error\nsignaling.\n\n### 6.2 The ? Operator\n\nThe ? operator is a central element of Oxide's error handling mechanism. It\nallows functions to handle errors without panicking. When applied to an\nexpression, the ? operator checks for errors and either returns the error or\nunwraps the value, depending on the outcome. This enables systematic error\npropagation.\n\n### 6.3 @bail Macro\n\nThe @bail macro simplifies the process of returning an error from within a\nfunction. It provides a convenient shortcut for returning an error at any point\nin the code, enhancing the manageability of exceptional cases.\n\n### 6.4 Custom Error Types\n\nOxide allows developers to define custom error types by implementing the Error\ntrait. This flexibility empowers developers to create tailored error types for\nspecific use cases, improving error handling precision. This can be done using\nthe @derive(Error); macro.\n\n### 6.5 Explicit Error Handling\n\nOxide encourages explicit error handling by design. Functions returning T? and\nthe ? operator make it clear that error handling is a natural part of the code.\nThis approach minimizes the potential for unexpected runtime panics and promotes\nsafer and more controlled error management.\n\n### 6.6 try { ... } Blocks\n\nThe try { ... } block provides a convenient syntax to implicitly apply the ?\noperator to all expressions that possibly contain an error within the block.\nThis simplifies error handling when multiple operations may return an error, but\nyou don't want to handle them all individually.\n\n```rust\nlet result = try {\n    let value = potentially_failing_operation(); // The ? is not required here.\n    value + 42\n};\n```\n\n#### Note\n\ntry blocks do not propagate the errors upwards towards the return of the\nfunction. Instead, they propagate the error to the result of the try block\nexpression. This means that a try block evaluates to T? and not T. If you do not\nwant to handle the error produced by a try block, you could still use the ?\noperator.\n\n```rust\n// Because we use the ? operator, the error from the try block\n// is propagated to the return of the enclosing function. That's\n// why the variable is named value and not result.\nlet value = try {\n    let value = potentially_failing_operation();\n    value + 42\n}?;\n```\n\n### 6.7 Pattern Matching on the Error Trait\n\nOxide allows developers to create custom error types and implement the Error\ntrait for them. These custom error types can be pattern matched for specific\nerror handling. This example illustrates the creation of a custom error type and\npattern matching:\n\n```rust\n@derive(Error);\ntype MyError(\n    description: string;\n);\n\nfn handle_error(error: Error) {\n    if let MyError(description) = error {\n        // Handle specific error\n    }\n}\n```\n\n# 7. The `??` Operator\n\nThe `??` operator in Oxide introduces a powerful and concise way to handle\nerrors by replacing them with alternative values. This operator is especially\nuseful when you want to provide a default or fallback value in case of errors,\nsimplifying error handling and making your code more readable.\n\n### 7.1 Usage\n\nThe `??` operator is used as follows:\n\n```rust\nlet value = some_operation() ?? fallback_value;\n```\n\nIn this expression:\n\n- If `some_operation()` succeeds and returns a valid result, `value` will be\n  assigned the value returned by `some_operation()`.\n- If `some_operation()` encounters an error, the `??` operator replaces the\n  error with the `fallback_value`.\n\n### 7.2 Benefits\n\nThe `??` operator offers several benefits:\n\n- **Simplified Error Handling:** It streamlines error handling by allowing you\n  to specify fallback values for specific operations.\n\n- **Clear Code:** Your code becomes more concise and easier to read, as you can\n  express error handling and fallback behavior in a single line.\n\n- **Reduced Error-Checking Code:** The `??` operator reduces the need for\n  extensive error-checking code when you don't care about the error and can\n  provide a sensible fallback.\n\n### 7.3 Examples\n\nHere are some examples of how to use the `??` operator in Oxide:\n\n```rust\nlet value = potentially_failing_operation() ?? 0;\n```\n\nIn this example, if `potentially_failing_operation()` encounters an error, the\n`??` operator replaces the error with the value `0`.\n\n```rust\nlet value = fetch_data() ?? load_default_data();\n```\n\nIn this example, if `fetch_data()` fails to retrieve data, the `??` operator\nloads default data using `load_default_data()`.\n\nThe `??` operator is a valuable addition to Oxide's error handling toolbox,\nenabling developers to handle errors with ease and precision while providing\nfallback values when needed.\n\n### Conclusion\n\nError handling in Oxide prioritizes safety, clarity, and reliability. By\nproviding systematic and standardized error handling techniques, the language\nempowers developers to create more robust and maintainable code while avoiding\nunexpected runtime errors. Oxide's approach to error management is simple, yet\npowerful, making it a valuable tool for application development.\n\n# 8. The `=\u003e` Syntax in Oxide\n\n## Introduction\n\nOxide introduces the `=\u003e` syntax as a convenient and expressive feature to\nstreamline code blocks that can be represented as single expressions. This\nformal specification provides an overview of the `=\u003e` syntax and its\napplications, enhancing code precision and readability.\n\n### 8.1 Single Expression Statements\n\nThe primary use of the `=\u003e` syntax is to simplify single-expression statements.\nIn Oxide, it allows developers to express these statements without the need for\n`{}` block delimiters.\n\n### 8.2 Function Definitions\n\nWhen defining functions with single expressions as their bodies, the `=\u003e` syntax\nbecomes a powerful tool for concise code. For example:\n\n```rust\nfn add(a: i32, b: i32) -\u003e i32 =\u003e a + b;\n```\n\nIn this case, the entire function body consists of a single expression\n(`a + b`). The `=\u003e` syntax eliminates the need for explicit `{}` blocks,\nproviding a more precise and clean representation.\n\n### 8.3 Conditional Statements\n\nThe `=\u003e` syntax can be employed to simplify conditional statements, such as `if`\nand `else` expressions.\n\n```rust\nif condition =\u003e do_something();\nelse =\u003e do_something_else();\n```\n\nHere, the `if` and `else` branches are single expressions (`do_something()` and\n`do_something_else()`). The `=\u003e` syntax streamlines the code, making it more\nreadable and less verbose.\n\n### 8.4 Error Handling Precision\n\nThe `=\u003e` syntax also plays a role in error handling. For functions that don't\ncare to explicitly handling errors, wrapping the entire function body in a `try`\nblock is a common practice.\n\n```rust\nfn do_something() -\u003e T? =\u003e try {\n    ... // We don't have to worry about any errors in this block.\n};\n```\n\nIn this example, the `try` block serves as the statement body, ensuring that\nerror handling is handled implicitly. The `=\u003e` syntax allows developers to\ncreate precise error-handling functions without the need for additional `{}`\nblocks.\n\n### Conclusion\n\nThe `=\u003e` syntax in Oxide offers a valuable tool for enhancing code precision and\nreadability. By simplifying single-expression statements, it allows developers\nto express code more concisely, reducing verbosity and providing a clean and\nprecise representation of functions, conditional statements, and error handling.\nOxide's `=\u003e` syntax contributes to a more efficient and expressive coding\nexperience, ultimately making the language more developer-friendly.\n\n# 9. Garbage-Collecting Shared References in Oxide\n\nOxide introduces a built-in type called `Gc\u003cT\u003e` that simplifies managing shared\nreferences with built-in cyclic garbage collection. This section provides an\noverview of `Gc\u003cT\u003e` and its advantages in handling shared data efficiently.\n\n### 9.1 The `Gc\u003cT\u003e` Type\n\n`Gc\u003cT\u003e` stands for \"Garbage-Collected\" and is a reference-counted smart pointer\nthat facilitates sharing data across multiple parts of your Oxide application.\nIt provides a concurrent reference counting mechanism that allows you to share\ndata efficiently while mitigating the risk of memory leaks caused by circular\nreferences.\n\n### 9.2 Automatic Interior Mutability\n\nOne of the primary features of `Gc\u003cT\u003e` is its ability to manage interior\nmutability. When a type `T` is wrapped in `Gc\u003cT\u003e`, Oxide implicitly wraps the\nfields of `T` in `RefCell` or `RwLock`, depending on whether `Gc\u003cT\u003e` is used in\na single-threaded or multi-threaded context. This automatic interior mutability\nmanagement ensures that concurrent access to shared data remains safe.\n\nWhen accessing fields from `Gc\u003cT\u003e`, Oxide returns a `F?` instead of `F`, where\n`F` represents the type of the field. This design choice reflects the shared\nownership of the data and the fact that Oxide cannot guarantee, at compile-time,\nadherence to the \"aliasing xor mutable\" rule. Instead, runtime mechanisms handle\nerror checking, and any operation on a field may potentially return an error (of\ntype `F?`) if the borrow checking rules are violated.\n\n#### Note\n\nAlthough accessing the fields of a `Gc\u003cT\u003e` returns `F?` (due to the inability to\ndo compile-time borrow checking), regular static borrow checking continues from\nthere. Since the compiler can determine that the borrow of a field from a\n`Gc\u003cT\u003e` was valid due to no error being returned, it can continue to do regular\nstatic borrow checking to ensure valid usage of the borrowed field `f`.\n\n### 9.3 Cyclic Garbage Collection in `Gc\u003cT\u003e`\n\n`Gc\u003cT\u003e` employs a reference counting mechanism to keep track of shared\nreferences and ensure proper memory management. When the reference count for a\nparticular piece of shared data reaches zero, it indicates that no active\nreferences exist. However, the responsibility for releasing memory associated\nwith `Gc\u003cT\u003e` is shared between reference counting and a cyclic garbage\ncollection mechanism.\n\n#### 9.3.1 Reference Counting\n\nReference counting in `Gc\u003cT\u003e` effectively tracks the number of active references\nto shared data. It precisely increases the count when new references are created\nand decreases it when references go out of scope or are no longer needed. When\nthe reference count drops to zero, it indicates that the shared data has no\nactive references. At this point, the object is freed immediately, and its\nassociated `drop` method is called, adhering to the same reference counting\nprinciples as a regular reference-counted type.\n\nThis immediate memory release ensures that Oxide applications efficiently manage\nmemory when references are no longer needed, without introducing any delay in\nthe process.\n\n#### 9.3.2 Cyclic Garbage Collection\n\nWhile reference counting efficiently manages individual references, it cannot\ndetect circular references within a group of `Gc\u003cT\u003e` objects. To address this,\nOxide incorporates a cyclic garbage collection mechanism. This collector is\nresponsible for identifying and releasing memory associated with reference\ncycles.\n\nWhen cyclic references occur, the cyclic garbage collector identifies them and\nintervenes to free the memory. By doing so, it ensures that `Gc\u003cT\u003e` remains a\nmemory-efficient solution for shared data management, even in the presence of\ncomplex reference relationships.\n\nThe combination of reference counting and cyclic garbage collection in `Gc\u003cT\u003e`\nprovides a comprehensive and reliable memory management strategy, ensuring that\nyour Oxide application remains both efficient and free from memory leaks.\n\n### 9.4 Using `Gc\u003cT\u003e` in Oxide\n\nTo use `Gc\u003cT\u003e`, you can wrap a type `T` using `Gc::new()`. Once wrapped, you can\nseamlessly pass `Gc\u003cT\u003e` across different parts of your Oxide application. It\nallows for sharing data without the complexity of lifetime management, offering\na straightforward solution for shared data scenarios.\n\n### 9.5 Implementation of the Copy Trait\n\nIn Oxide, the `Gc` type implements the `Copy` trait. This means that `Gc`\ninstances are implicitly cloned when moved, and their reference counts are\nincreased accordingly. The `Copy` trait ensures that `Gc` behaves consistently\nwith other `Copy` types in the language, providing a convenient and efficient\nway to handle reference counting for shared data.\n\n### 9.6 Example\n\nHere's a simple example of using `Gc\u003cT\u003e` to share data in Oxide:\n\n```rust\nuse std;\n\n@derive(Debug);\ntype SharedData(value: i32);\n\nfn main() -\u003e ? =\u003e try {\n    let shared = Gc::new(SharedData(value: 42));\n\n    std::io::println(`The shared value is {shared.value:?}`);\n};\n```\n\nIn this example, `Gc::new()` wraps the `SharedData` type, and you can access its\nfields without needing to manage explicit borrows, thanks to the automatic\ninterior mutability provided by `Gc\u003cT\u003e`.\n\nWith `Gc\u003cT\u003e`, Oxide streamlines shared data handling and ensures your\napplication remains memory-efficient and free from common issues related to\nshared data management.\n\n# 10. The Copy Trait in Oxide\n\nIn Oxide, the `Copy` trait is a fundamental concept governing the behavior of\ntypes when they are moved. Unlike Rust, where the `Copy` trait means that a type\ncan be directly duplicated through memory copying (e.g., `memcpy`), Oxide\ninterprets the `Copy` trait differently.\n\n### 10.1 Implicit Cloning on Move\n\nIn Oxide, a type marked as `Copy` doesn't necessarily support low-level memory\ncopying but indicates a different behavior. When you move a `Copy` type, Oxide\nimplicitly clones it instead of transferring ownership. This means that the\noriginal value remains intact and accessible in its original location, while a\nnew copy of the value is created at the target location. This behavior ensures\nthat changes to one instance of the value do not affect others, allowing\ndevelopers to work with data efficiently while maintaining the integrity of the\noriginal.\n\n### 10.2 Interactions with the Clone Trait\n\nIn Oxide, when implementing the `Copy` trait, there's no need for explicit\n`Clone` trait implementations; developers can use `@derive(Clone)` for\nconvenience. However, it is important to note that the `Clone` trait is\nimplicitly assumed to be implemented when defining `Copy`, as `Copy` relies on\nthe cloning mechanism to perform implicit cloning during moves.\n\nIt's important to emphasize that the reverse relationship does not apply. While\na type can implement `Clone` and have the ability to clone explicitly, this does\nnot necessarily imply that the type can implement `Copy`. The `Copy` trait is\nreserved for data types that are implicitly cloned on move, ensuring that they\nbehave consistently with other `Copy` types.\n\n### 10.3 Simplified Data Sharing\n\nThe distinction between `Copy` and `Clone` traits in Oxide contributes to more\nstraightforward data sharing. By understanding the implicit cloning behavior of\n`Copy` types on move, developers can manage shared data with minimal effort and\nwithout the need for manual cloning operations. Oxide's approach to the `Copy`\ntrait streamlines data handling, promoting efficient and reliable code\ndevelopment.\n\n# 11. Examples\n\n### 11.1 Error Handling\n\n```rust\n// main.ox, explicit error handling with ? operator.\n\nuse std; // This imports the std namespace.\n\n@derive(Debug); // This automatically implements the Debug trait for type Foo.\ntype Foo (\n    bar: i32,\n);\n\n// This implements the add method for type Foo.\nfn Foo.add(self, rhs: i32) -\u003e Self\n    =\u003e Self(bar: self.bar + rhs);\n\n// The ? return type means that the main function may return an error (but not a value).\nfn main() -\u003e ? {\n    let foo = Foo(bar: 41) + 1; // This works because Foo properly implements the Add trait.\n    \n    // io is from the std namespace. since we imported it, we do\n    // not have to use the fully-qualified name (std::io::println).\n    io::println(`{foo:?}`)?; // The ? operator propagates any errors returned by println (if there was one).\n}\n```\n\n```rust\n// main.ox, implicit error handling with try block.\n\n...\n\n// Since we might not want to handle the errors in the main function, we can use an arrow body\n// instead and wrap the whole function in a try block.\nfn main() -\u003e ? =\u003e try {\n    let foo = Foo(bar: 41) + 1;\n\n    // As you can see, we do not need the ? operator\n    // here as it is inside a try block.\n    io::println(`{foo:?}`);\n}; // We need a semicolon because a try block is a statement.\n```\n\n```rust\n// main.ox, handling a specific error (using trait pattern matching)\n\nfn main() -\u003e ? {\n    let file = std::fs::open(\"./example.txt\");\n\n    if let Err(FileDoesntExist(path)) = file {\n        std::io::println(`The file {path} does not exist.`)?;\n    } else {\n        // Do something with the file.\n    }\n}\n```\n\n# 12. IDE and Tooling Support\n\nOxide is designed not only with the language itself in mind but also with robust\ntooling and development environments. Its official compiler, found under the\n\"oxide-lang::compiler\" module, provides a straightforward and efficient API for\ndevelopers, enabling the creation of powerful IDEs and various tools.\n\n### 12.1 The Oxide Compiler API\n\nThe Oxide Compiler API allows developers to interact with the compiler\nprogrammatically, making it a valuable tool for building integrated development\nenvironments (IDEs) and other code-related applications. The API provides a\nsimple yet comprehensive interface to analyze, manipulate, and work with Oxide\nsource code. Here's a basic example of how to use the API:\n\n```rust\nuse oxide_lang::compiler::{Compiler, Document};\n\nfn main() { let mut compiler = Compiler::new();\n\n    compiler.mutate(|state| {\n        state.add(Document::new(\n            \"main.ox\",\n            \"fn main() -\u003e ? { ... }\",\n        });\n    });\n\n    let snapshot = compiler.snapshot();\n    let model = snapshot.get_semantic_model();\n\n    println!(\"{:#?}\", model);\n}\n```\n\nKey features of the Oxide Compiler API include:\n\n#### **Incremental Compilation**\n\nThe compiler is designed with incremental compilation in mind. It ensures that\nonly the necessary parts of the code are analyzed and recomputed when changes\nare made, making it exceptionally fast. Snapshots, created from the compiler,\nprovide access to semantic models, and they are both lazy and thread-safe.\n\n#### **Concurrency**\n\nThe compiler API is designed with concurrency in mind. It allows for multiple\nsnapshots to be queried concurrently. Older snapshots can coexist with newer\nones, even while being queried in parallel. This level of concurrency and\nparallelism ensures that developers can build high-performance and efficient\ndevelopment tools.\n\n#### **IDE Integration**\n\nIDEs can leverage the Oxide Compiler API to offer features such as code\nanalysis, autocompletion, error checking, and more. The incremental nature of\nthe compiler and the rich semantic models provided by the snapshots enable IDEs\nto offer real-time feedback and enhance the development experience for Oxide\nusers.\n\n### 12.2 Compiler Plugins\n\nA fundamental feature of the Oxide language is the support for compiler plugins\nor extensions. These plugins, similar to popular tools like Vite and Webpack for\nJavaScript, can extend the capabilities of the Oxide compiler. They can provide\nadditional functionality for tasks like code optimization, bundling, and more.\nThe unique aspect of Oxide's approach to compiler plugins is that they are\ncompiled to WebAssembly (Wasm) and executed using the Wasmtime runtime.\n\nThis approach has several advantages:\n\n- **Dynamic Compilation**: Compiler plugins can be dynamically executed at\n  compile-time. This dynamic nature allows for versatile and customizable build\n  processes without needing to modify the core compiler.\n\n- **Safety**: Plugins run in a WebAssembly sandbox, ensuring that they do not\n  have access to sensitive parts of the system. This approach enhances security\n  while offering extensibility.\n\n- **Versatility**: Compiler plugins enable the community to extend the\n  capabilities of Oxide for various development and deployment scenarios, from\n  optimizing code to customizing build pipelines.\n\n### 12.3 Versatile Tooling\n\nWith a robust compiler API and support for compiler plugins, the Oxide language\nencourages the development of versatile tooling. IDEs, code linters, formatters,\nand project builders can be created or enhanced with ease. The incremental\ncompilation and plugin system further empower tooling developers to offer\nefficient and feature-rich solutions for the Oxide ecosystem.\n\nOxide's commitment to IDE and tooling support ensures that developers have the\nnecessary tools to write, analyze, and deploy Oxide code efficiently. This\napproach not only simplifies the development process but also fosters a thriving\necosystem around the language.\n\nBy providing a powerful and versatile compiler API and enabling dynamic compiler\nplugins, Oxide aims to support a wide range of developer needs and create a\nseamless development experience.\n\n# 13. Final Thoughts\n\nThis section may not be as thorough or correct as other sections as I wrote it\nat the last minute.\n\n### 13.1 Structs in Oxide\n\nAlthough I didn't write a section specifically about it, structs in oxide are\nsimply defined as types with (optionally) named arguments. For example,\n\n```rust\ntype Foo(bar: i32);\n\n// is equivalent to (in rust)\n\nstruct Foo {\n    bar: i32,\n};\n```\n\nand\n\n```rust\ntype Foo(i32);\n\n// is equivalent to (in rust)\n\nstruct Foo(i32);\n```\n\nAlso, types (structs) can be zero-sized as well (just like rust)\n\n```rust\ntype Foo;\n\n// is equivalent to (in rust)\n\nstruct Foo;\n```\n\nThe reasoning behind this isn't necessarily complete. I just thought the syntax\nwas simpler and also was somewhat future proofed to allow potentially more ways\nto define types (for example, algebraic types (tagged unions) are defined in a\nvery similar way, `type Foo = Bar | Baz;`).\n\nI guess another reason is that when constructing `Foo`, it acts basically like\ncalling a function. For example,\n\n```rust\ntype Foo(bar: i32);\n\nfn main() {\n    // This is like calling a constructor on an object in many languages.\n    let foo = Foo(bar: 42);\n}\n```\n\nI suppose this also calls into question whether functions themselves should be\nallowed to have named arguments. For example,\n\n```rust\nfn foo(bar: i32) { ... }\n\nfn main() {\n    // I think this should be allowed, I don't see why not?\n    foo(bar: 42);\n}\n```\n\n### 13.2 Concurrency\n\nI totally forgot to even mention concurrency (mainly because I don't really\nknow). I don't really want the language to have async/await. I was thinking of\nsomething closer to go with green threads. I was also possibly thinking about\nthe actor-model instead (maybe the entry point of the program would be an actor\ninstead of the main method?).\n\n### 13.3 Macro Code Generation\n\nI was thinking that Oxide could implement something similar to the quote crate.\nIt would be built-in and developed alongside the language. It would allow for\nthe dynamic creating of code at compile-time while still be statically checked\nby the compiler to be a valid code transformation. For example,\n\n```rust\n// This macro does not replace the input Type, but instead\n// generates a Method for it at compile-time. Also, remember\n// that the Type object contains the full semantic information\n// about the Type itself. It's not just a token stream.\n@marco();\nfn some_macro(type: Type) -\u003e Method {\n    @code {\n        fn (#type.ident).foo(\u0026self) -\u003e Bar {\n            // ...\n        }\n    }\n}\n```\n\nThis example isn't meant to be correct or represent what it would actually be\nlike to create a macro like this in oxide. Instead, it's just meant to be a\ngeneral idea on how it could work.\n\nAlso, I'd like to point out that macros would somehow need to be compiled and\nrun at compile-time. This could potentially be done using wasm like I envisioned\nfor compiler plugins.\n\n### 13.4 Namespaces\n\nPersonally, I'm not a huge fan of modules in languages (including rust). I think\nthey are too messy. Instead, I like how c# manages code grouping. At the start\nof an oxide file you can (must) declare it's namespace.\n\n```rust\n// example.ox\nnamespace Example;\n```\n\nAll types, methods, functions, and traits implemented in the Example namespace\nare available to this file without any using statements (this includes\nnon-exported members). It's like all files in the Example namespace are just one\nlarge file.\n\nIf you want a member to be private, you simply don't declare it public.\n\n```rust\nnamespace Example;\n\n// Foo is private to the Example namespace. Not even other\n// namespaces in the same project can access it.\ntype Foo; // To make it public simply do: pub type Foo;\n```\n\nIf you want to allow all namespaces in this project to access Foo, but not\npeople using this library then we can define it as pub(this) (or something like\nthat).\n\n```rust\nnamespace Example;\n\n// pub(this) means that Foo is public only to this project.\npub(this) type Foo;\n```\n\n### 13.5 String Interpolation\n\nI want string interpolation to be a built-in construct in oxide (instead of the\nformat! macro). It will likely use the `` tokens. The way it works is fairly\nsimple. When you want to interpolate a value, you simply do:\n\n```rust\n// This is a string interpolation. It doesn't necessarily\n// return a string, but instead works with a formatter.\n`The value is {value}.`\n```\n\nTo provide formatting options, you simply separate the value and options by the\n\":\" token.\n\n```rust\n`The value is {value:options}.`\n\n// For example, if you want to format with the Debug trait instead...\n\n`The value is {value:?}.` // The ? option signifies the use of the Debug trait, instead of the Display trait.\n```\n\n### 13.6 Arithmetic\n\nTechnically arithmetic in any language is inherently unsafe. This is due to the\npotential for overflows, underflows, loss of precision (although this might not\ncount), ...\n\nI think the best way to account for this in Oxide (since it doesn't allow for\npanicking) is to make all arithmetic operations return T? (e.g. i32?), but for\nthem to be implicitly returned from the function. This means, to do any math at\nall, the function must return T?. For example,\n\n```rust\n// Since a + b might overflow (or underflow),\n// the return type must be i32?.\nfn add(a: i32, b: i32) -\u003e i32? =\u003e a + b;\n```\n\nThankfully, if you don't want arithmetic to always bubble up to the return of a\nfunction, you can wrap it in a try block (like anything that returns an error).\nThis allows you to break the arithmetic up and to potentially handle different\ntypes of errors. For example,\n\n```rust\nfn foo(a: i32, b: i32) -\u003e i32 {\n    let result = try { a + b };\n\n    if let Err(error) = result {\n        if let IntegerOverflow = error {\n            // Do whatever on an integer overflow.    \n        }\n    } else {\n        // No error adding values.\n    }\n}\n```\n\nNormally, though you would just ignore the errors and let them automatically\npropagate up to the return of the function.\n\n#### Note\n\nThere might be other operations that implicitly bubble the error up to the\nreturn. Potentially, it could even be a trait or something to allow other types\nof operations to do this. Or maybe a function attribute (like @bubbles or\nsomething). For example,\n\n```rust\n// The bubbles attribute tells the compiler that the error result of\n// this function should be implicitly bubbled to the return of the caller.\n@bubbles(); // Probably @propagates();\nfn add(a: i32, b: i32) -\u003e i32? =\u003e a + b;\n\nfn main() -\u003e ? {\n    // As you can see in this example, I do not use the ? operator\n    // after the add function call. The reason is that the error\n    // automatically bubbles up to the return type of the function.\n    // This is why main returns ? (other than the println function\n    // also potentially returning an error).\n    std::io::println(`1 + 2 = {add(1, 2)}`)?;\n}\n```\n\nOther potential names for the attribute includes `@propagate` (maybe\n`@propagate(Error)` to make it more generic?) ...\n\n### 13.7 Oxide Scripting Language\n\nI just realized that there was a project called \"oxide-lang\" that was a\nscripting language inspired by rust. This project has nothing to do with that\nother than having the same name (tbh, it's a fairly generic rust related name).\n","funding_links":[],"categories":["Rust","\u003ca name=\"Rust\"\u003e\u003c/a\u003eRust"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNoahGav%2Foxide-lang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNoahGav%2Foxide-lang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNoahGav%2Foxide-lang/lists"}