Git Product home page Git Product logo

boba's Introduction

Boba - A Structured Concatenative Language

Boba is an early-stage, general purpose concatenative programming language.

Key features include:

  1. Expressive, mostly implicit static types and kinds
  2. Language-incorporated unit and property tests + runners
  3. Algebraic effects via scoped effect handlers
  4. Algebraic data types and pattern matching on constructors
  5. Compile-time resolved function overloading
  6. Structurally typed tuples, records and variants
  7. Byte-code VM-in-Go backend with straight-forward first-order FFI access
  8. Familiar looping, branching, and variable definition syntax constructs

Hailstone Example

func is-even n = n 2 rem-i32 0 eq-i32

about :
/// The `hailstone` function is sometimes named that because of how the values
/// 'bounce' up and down (like hail in a storm cloud) as the sequence computes.
rec func hailstone n =
    switch {
        | n 1 eq-i32 => []
        | n is-even  => n 2 div-i32 hailstone
        | else       => 3 n mul-i32 inc-i32 hailstone
    }
    n cons-list

test hailstone-1? = 1 hailstone is [1]
test hailstone-2? = 2 hailstone is [1, 2]
test hailstone-3? = 3 hailstone is [1, 2, 4, 8, 16, 5, 10, 3]
test hailstone-6? = 6 hailstone is [1, 2, 4, 8, 16, 5, 10, 3, 6]

export { hailstone }

See the test/ folder for many more examples of Boba syntactic and semantic features.

Building from source

The Boba compiler is currently implemented in F#. Recommended to have both .NET 6 and Go language version 1.18 installed on the system before building. This repository is Gitpod compatible and will automatically create a container capable of building and running the compiler.

Example build-and-run command:

dotnet run --project Boba.Compiler compile test/while-expr

This will build the compiler, then compile the test/while-expr.boba file in the tests directory into an executable and then run it.

To use Boba's inline testing features, simply replace compile with test:

dotnet run --project Boba.Compiler test test/ackermann

This will run all the tests present in the test/ackermann.boba file and report on their success/failure.

To run a test or program without the current runtime debug trace, include a release flag:

dotnet run --project Boba.Compiler test test/hailstone release

Installation

Installers and binary packages are not yet available while the compiler CLI further stabilizes.

License

Boba is available and distributed under the terms of the MIT license. See LICENSE for details.

Roadmap

In no particular order, and missing some potential work that may take priority:

  • Community feature: Better showcase examples
  • Ecosystem feature: Flesh-out primitives library further
  • Codegen feature: Compile some Boba functions to Go rather than byte-code

boba's People

Contributors

jcmrva avatar robertkleffner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

boba's Issues

Email spam coming from this repo

Hello,

It's the first time I hear about glossopoeia/boba and it wasn't nice at all. I never starred, forked and/or participated in any other way in this repo. Nevertheless, I've got a spam email with the following content:

image

I hope it wasn't intentional.

Separate variable frame stack from handler stack

There is currently a semantic issue when a handle expressions that calls a handler, then resumes, then tries to reference a variable from a scope outside the handle expression.

Example of something that shouldn't work right now:

let x = 1;
handle {
    hdlr! x
} with {
    hdlr! => ;
}

Overload condensing rule declarations

So something like

Empty a, Semigroup a ==> Monoid a

Helps reduce the number of constraints in printed types, and also allows for writing simpler types.

Eager addition of propagation rules to type environment

Currently propagation rules are added to the type environment as they are encountered in the source code during compilation. This means that 'orphan' rules may not be available during type inference if a function that relies on the overloads is defined prior to the 'orphan' rule.

This problem is already solved for instances (and 'orphan' instances), so we should be able to use a similar method to gather rules, with the caveat that rules are not specific to a particular overload: a rule should only be added to the type environment if all of the constraints in the head and result components are defined. The last-defined overload will be the one that includes all rules that mention it.

Support multiple iterators in for loop assign clauses

Currently only one is supported due to compilation trickiness, would like to resolve this and support more than one.

Since iterator for loops are compiled down to handlers maybe there is a straightforward way to do this with injection.

Or maybe a dotted effect? That would be interesting, semantics could probably handle it but the type system might not.

Worst case scenario it can be done with a few definitions for multiple argument iterators, i.e. yield1!, yield2!, etc.

Renamer: Partial re-export of names is slower than it needs to be

Currently it finds all re-exported names (recursive for all imported units as well) and then filters to just the explicitly re-exported names.

Instead, it should look at the explicitly re-exported names, and find the base declaring unit of the name. Still recursive but no longer builds a (potentially big) tree only throw most of it away.

Infer sharing and validity constraints for data constructors

Currently, the programmer has to specify the sharing and validity constraints manually in constructor types for the compiler to get them right. For instance, instead of writing

rec data List a
    = Cons (List a) a --> (List a)
    | Nil --> (List a)

the programmer has to write

rec data List x
    = Cons {(List {a vd sd}) (vd & vl) (sd | sl)} {a v s} --> {(List {a (vd & v) (sl | s)}) (vd & v & vl & vr) (sd | sl | s | sr)}
    | Nil --> {(List {a v s}) (v & vr) (s | sr)}

Writing out the latter is tedious and easy to get wrong. But it's very pattern based and seems like it should be easy to automatically generate.

The concerns with automating this are:

  1. Will it be polymorphic enough?
  2. Will it be too polymorphic?
  3. Can the programmer plug in some custom sharing attributes without breaking it?

`resume` does not leave unconsumed items on the stack like it should

This is a particular problem with multishot effect handlers, which may require more lets than necessary to compose nicely.

For instance, in the amb! tests in the examples folder, the handler:

flip! => False resume True resume append-list

must currently be written as:

flip! => {
            let x = False resume;
            let y = True resume;
            x y append-list
        }

due to the semantics not having enough information to determine how many values should be passed back to the continuation stack.

We can use the type declaration of the effect to determine how many values should be passed back.

Instance type match check is too strict

Problem is found in the typeable test file.

Core issue is that a perfectly valid instance definition can infer a type that is actually more general than the overload. In this case we can let it pass, but we need to be careful that the instance actually consumes and produces the expected number of elements from the stack still. If it does not consume and produce enough, any function which uses that instance will be unsoundly typed.

Context ambiguity checking

Currently ambiguity checking is a no-op, which means that the compiler will try to generate code for ambiguous functions. Seems like it fails to do so in a lot of cases, which is good, but it also fails with unclear error messages or guidance toward resolving the problem.

Implement an explicit ambiguity check that at least points to the function that has the type problem.

Overload function signatures should accumulate effects, perms, totality of instances

Right now it is possible to 'leak' effects out of the type system by hiding effectful calls in overloaded function instances. It is up to the overload designer to predict what effects will be needed and define them in the overloaded function type signature, which is obviously asking for big trouble.

There's two options here:

Option 1: Ban non-declared effects from escaping an instance.

Basically akin to throwing up our hands and waving the white flag. Tempting from a compiler developer perspective, but I just know this will eventually cause complaints about usability. It is, however, easy to make sound and straightforward to think about for the average developer: just 'handle' any effects that need to be handled, and wrap them in a result type if you really want them to escape.

Option 2: Join all inferred effects, permissions, and totality attributes inferred on instances into the overload effect/permission/totality.

We should remove effect, permission, and totality attributes from appearing in the overload function definition at all. Only input and output sequences will be allowed, and only those will affect instance overlap checking and inferred type matching.

During instance gathering, the inferred effect, permission, and totality of each instance will be unified together into one large effect type for overall overload method signature.

`test` should handle comparisons of multiple outputs

So something like:

test multiple-out? = 1 2 is 3 2 should type check, but partially fail at runtime with 1 != 3.

This should be straightforward to implement with the gather method as a simple pre-type-checking code transformation in the test code generator.

Functional dependencies can cause compiler to 'freeze'

Type inference for functions that have multiple functional dependency constraints can cause the compiler to hang in the CHR implementation. I don't believe it is looping, but it is ineffeciently computing all possible reductions. The reason the compiler pursues all possible reduction paths, instead of choosing one, is to verify that the rule set is confluent, since we have no way to do that before the rule set is run currently.

String interpolation

It's convenient enough to miss it a lot of times and makes many simple string literals more readable. But it would be nice if this was just a syntactic sugar of some sort, driven by an overload or something.

So, $"Hello there, {name}, and welcome!" would be desugared into "Hello there, " name show concat-string ", and welcome!" concat-string.

Primitive iterable fixed-size ordered collection type

Will make use of the fixed-size Abelian type defined in Boba.Core

  1. Iterable in for loops
  2. Special syntax for creating with compile-time known size
  3. Primitive functions for accessing/setting elements while maintaining uniqueness

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.