• Stars
    star
    305
  • Rank 136,879 (Top 3 %)
  • Language
    Rust
  • Created over 8 years ago
  • Updated over 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A high-level language for Rust

Lia: A High-Level Language for Rust

ä¿© (liÇŽ) - 1. two, 2. a pair

Lia is a dynamically typed and garbage collected programming language that seamlessly interoperates with Rust by using Rust as a compile target. This enables Lia users to drop down into efficient Rust code when necessary, but work with a high-level Javascript-esque language for the majority of their application. For example, binding to a matrix library (à la numpy) is simple:

// lia! declares a set of Lia functions. It is a procedural macro that compiles Lia into Rust.
lia! {
    function multiply_matrices() {
        console.log("Multiplying matrices");
        var x = @Matrix::from_list([[4, 3], [2, 1]]); // The @ means a foreign (Rust) function
        var y = @Matrix::from_list([[1, 2], [3, 4]]);
        var z = @Matrix::multiply(x, y);
        return @Matrix::get(z, 0, 0);
    }
}

#[derive(Clone)]
struct Matrix {
    data: Vec<i32>,
    rows: i32,
    cols: i32,
}

// Putting #[lia_impl_glue] on an impl block will automatically generate functions that
// do the appropriate type-casting from Lia's dynamic types into Rust's static types.
#[lia_impl_glue]
impl Matrix {
    // some functions omitted...

    pub fn multiply(&self, other: &Matrix) -> Matrix {
        assert!(self.cols == other.rows);

        let mut new_mat = Matrix::new(self.rows, other.cols);
        for i in 0..self.rows {
            for j in 0..other.cols {
                let mut dot = 0;
                for k in 0..self.cols {
                    dot += self.get(i, k) * other.get(k, j);
                }
                new_mat.set(i, j, dot);
            }
        }

        return new_mat;
    }
}

fn main() {
    // Lia includes macros that simplify handling Lia functions and values in Rust.
    let result: LiaAny = call!(multiply_matrices());
    cast!(let num: i32 = result);
    assert!(num == 13);
}

Using Lia

Look at lia-tests/src/lib.rs for example usage. Right now Lia is still in its early stages and requires nightly to build so it's not ready for prime time, but I encourage you to try it out and bring up any suggestions in an issue/PR or email me at [email protected].

To use Lia in your own code, first switch to Rust nightly with multirust override nightly. Then add to your Cargo.toml:

[dependencies]
lia = "0.1.0"
lia-plugin = "0.1.0"

Then in the file you want to use Lia:

#![feature(plugin, box_syntax)]
#![plugin(lia_plugin)]

#[macro_use]
extern crate lia;

use lia::runtime::*;

lia! { ... }

Motivation

One of the biggest challenges facing programming languages today is interoperability. As PLs both proliferate in ever increasing numbers and specialize to application domains, many programmers need the ability to work effectively across languages. For example:

  • Python has a plethora of bindings to C, CUDA, etc. for efficient operations on numeric or image data. This is implemented in libraries like numpy and scipy.
  • Javascript recently saw the rise of React which enabled seamless integration of HTML and Javascript in their new JSX format (this is very similar to the procedural HTML generation that popularized PHP many years earlier).
  • A number of domain-specific languages, e.g. Halide, have popped up in recent years with varying levels of interoperability to existing general-purpose programming languages.

Many of the problems with interoperability between two languages can be solved by sharing a common type system. Even if two languages have a different runtime and different syntax, if they merge at the type-level then it's considerably easier for them to work together. For example, Terra addressed this problem by modifying Lua to have code generation facilities for a C-like language and then using LuaJIT's FFI as the type system glue between the two languages. However, this approach necessitates that the two languages (Lua and Terra) have separate runtimes and relies on a fragile layer of type glue between Lua's types and what is effectively C's types.

Lia takes a different approach: instead of separating the high level interpreted runtime and the low level compiled runtime, we compile Lia code into Rust code. That way it shares the same type system and runtime and enables seamless interoperability between the two languages. Lia raises the level of abstraction over Rust by eliminating lifetimes/memory management as well as static typing.

System description

type LiaAny = Rc<RefCell<Rc<RefCell<Box<Any>>>>>;

All values in Lia (integers, closures, etc.) have the type LiaAny. We'll walk through the components of the above type to understand Lia's layers of abstraction.

Eliminating memory management

The burden of managing memory/lifetimes is shifted from the programmer at compile time to the program at runtime via reference counting (type Rc). Ideally this would be a garbage collected pointer (like this one) that won't leak memory when cycles are induced, but that can come later. The Rc wraps a RefCell to allow the enclosed value to be mutated.

A simple implementation of a runtime-managed type could be just Rc<RefCell<T>>. However, we need a second layer of indirection because variables can be reassigned. Consider the following example:

var x = 1;
x = 2;

With the naive implementation, this could compile as:

let mut x = alloc(1);
x = alloc(2);

However, we do not want to rely on the mutability of Rust variables (specifically the names, e.g. x, not the values) to enable the mutability of Lia. This arises when we consider the interaction between closures and mutability:

var x = 1;
(function(){ x = 2; })();
x = 3;

Our above scheme would compile into:

let mut x = alloc(1);
(move || { x = alloc(2); })();
x = alloc(3);

And the compiler will complain on the third line that x is already moved into the closure. Instead, we have each Rust name correspond to a slot that contains a value. The above example actually compiles into roughly:

let x = alloc(alloc(1));
let x_copy = x.clone();
(move || { *x_copy.borrow_mut() = alloc(2); })();
*x.borrow_mut() = alloc(3);

The compiler creates the copies by finding variables closed by the Lia closures and creating copies. In sum: to have both runtime-managed memories and closures, each value is wrapped in two layers of Rc<RefCell<...>.

Eliminating static typing

To make the language dynamically typed, each underlying value in the slots discussed above has type Box<Any>. The Any type represents a value of any possible type, and can be checked/casted at runtime. The Box<...> ensures that the type is a fixed size (similar to the OCaml runtime, where each value is the size of a pointer).

Casting in to the generic type is done with lia::runtime::alloc that does all the proper allocations. Casting out uses the cast! macro. Cast semantics depend on whether the type being casted into is owned or borrowed. The Any type from the standard library provides downcast methods that return a reference to the enclosed value when casted correctly. So when casting a LiaAny into a reference type, the cast! macro just uses the given reference. However, if you want to cast into an owned type, then the referenced value gets cloned. This is because it would be unsafe to take ownership from the Lia runtime, as Lia makes no guarantees on the number of references to a value at any point in time.

TODO list

  • Add JIT for dynamic execution
  • Make errors clear on both the Lia level (Spans) and Rust level (code maps?)
  • Import remaining language constructs
    • Loops
    • Rest of the binops
    • Modules

More Repositories

1

flowistry

Flowistry is an IDE plugin for Rust that helps you focus on relevant code.
Rust
1,812
star
2

tyrade

A pure functional language for type-level programming in Rust
Rust
305
star
3

terracuda

A high-level Lua API for GPU parallelism [15-418 final]
Perl
64
star
4

indexical

Human-friendly indexed collections
Rust
45
star
5

sevenwonders

The popular Seven Wonders board game on the web
PHP
40
star
6

corrset-benchmark

A repository to test different performance optimizations in a sparse matrix computation.
Jupyter Notebook
37
star
7

rabbot

Abstract binding tree code generator
Rust
35
star
8

inliner

Programmable, human-readable inlining of Python code
Python
29
star
9

rustc-type-metaprogramming

Rust
24
star
10

cmu-grades

Gets your grades from CMU services
Python
21
star
11

wordtree

A Python library for generating word tree diagrams
Python
20
star
12

learn-opengl-rust

Learn OpenGL in Rust with web compatibility
Rust
16
star
13

model-js-workspace

My personal standard for how to set up a Javascript workspace
TypeScript
13
star
14

rust-book-exercises

Rust
12
star
15

types-over-strings

Rust
12
star
16

pyro-under-the-hood

Jupyter Notebook
8
star
17

cargo-single-pyo3

Generate a Python module from a single Rust file.
Rust
8
star
18

hyperlapse

Open-source implementation of MSR Hyperlapse
C++
8
star
19

gcp-job-queue

Python
7
star
20

r-autota

A tool to make R error messages easier to understand
HTML
6
star
21

rust-editor

Rust
6
star
22

web-logger

A prototype Rust logger that uses the browser instead of the console.
Rust
5
star
23

411-rust-starter-code

Rust
5
star
24

example-analyzer

Rust
5
star
25

rust-api-type-patterns

5
star
26

algo-rs

Assorted algorithms implemented in Rust
Rust
4
star
27

simple-bind

One-line non-exhaustive binds in Rust
Rust
4
star
28

aoc2021

q
4
star
29

dotfiles

My emacs configuration
Emacs Lisp
4
star
30

cmu_auth

Simple Python methods for authenticating to CMU services
Python
4
star
31

so-lang-diversity-analysis

Analysis of diversity in language communities based on the Stack Overflow Developer Survey 2022
Jupyter Notebook
3
star
32

expressiveness-benchmark

Jupyter Notebook
3
star
33

dml

Derma Markup Language, for HTML-style UI creation (for Garry's Mod)
Lua
3
star
34

classity

Remote lecturing tool [hackathon project]
CSS
3
star
35

y0

A language?
OCaml
3
star
36

tquery

jQuery, but for types
Rust
2
star
37

psypl-experiments

Jupyter Notebook
2
star
38

nota

JavaScript
2
star
39

gentle_whisper

Automatic transcription + fine-grained time-alignment
Python
2
star
40

tacticalassault

Class-based FPS gamemode (for Garry's Mod)
Lua
2
star
41

mdbook-preprocessor-utils

Rust
2
star
42

pickle-cache

Small utility for easily and efficiently saving/loading Python values to disk.
Python
2
star
43

utilikilt

CMU services on your phone
Objective-C
2
star
44

autoplan

Jupyter Notebook
2
star
45

glen

Three.js extension for creating games in the browser.
JavaScript
2
star
46

willcrichton.github.io

Github site
HTML
2
star
47

react-sequence-typed

TypeScript
2
star
48

rustc-embed

Rust
1
star
49

walrus-locator

In search of places to put a walrus
1
star
50

crashcourse-chat

For CrashCourse at CMU
JavaScript
1
star
51

bevy_world_visualizer

Rust
1
star
52

lagooned

A game of exploration and mystery
JavaScript
1
star
53

tetedoro

JavaScript
1
star
54

context-var

React-style context variables (i.e. dynamic scoping)
Python
1
star
55

semantic-divergence

Rust
1
star
56

fishies

A Paper.js game about fish. [98-232 sample project]
JavaScript
1
star
57

rlu-rs

A Rust implementation of the Read-Log-Update concurrency mechanism
Rust
1
star
58

notifier

Python
1
star
59

lecture-intelligence

A tool for scraping and analyzing lecture video viewing data from Panopto.
Jupyter Notebook
1
star
60

hudd

HUD Designer - intuitive menu creator for Garry's Mod
Lua
1
star
61

awap-2015

Official repository for Algorithms with a Purpose 2015: Startup Tycoon
Python
1
star
62

regulair

Regex library for FPGAs
Python
1
star
63

status-protect

Chrome extension designed to folly malicious Facebook status-updaters
JavaScript
1
star