Skip to content
/ bisquit Public

Bisquit is an exercise in building a statically typed functional programming language with type inference.

Notifications You must be signed in to change notification settings

minond/bisquit

Repository files navigation

Bisquit

Bisquit is an exercise in building a statically typed functional programming language with type inference. To get started, install sbt (https://www.scala-sbt.org/1.x/docs/Setup.html) and run sbt run.

When you start the repl, all of the declarations from the Prelude module will be imported and exposed to the scope. Doing this allows you to use all of the builtins which let you do arithmetic, boolean logic, list manipulation, and ref cell manipulation.

> import Prelude exposing (...)
< ok

Bisquit's type system implements generics. Types can also be extended and used as constraint in a generic type:

> +
= <fn> : a -> a -> a where a < Num

> eq
= <fn> : a -> b -> Bool where a < Ord, b < Ord

Note + wants two parameters of the same generic type, a, while eq accepts parameters of different types, as long as they both extend Ord. We can see what Bisquit does at compile time when this constraint is not met:

> +(3, 45)
= 48 : Int

> +(3, "one")
type error: given a value of type Str where a value of Int was expected:

  <repl>:1 | +(3, "one")
                  ^

Notice the half decent error message. Not bad huh? Now let's show how a len function would be written for objects with a list property within it:

> def len(obj) =
    if nil?(obj.list)
    then 0
    else +(1, len(cdr(obj.list)))
: { list : List[a] } -> Int

> len({ list = [1, 2, 3] })
= 3 : Int

Bisquit has structural typing, so the inferred type of len ends up being { list : List[a] } -> Int. A named type can be used as a constraint, but otherwise only its shape will be used and checked by the typechecker.

The last example I'll show is that of a ref cell. All declarations are immutable and are only shadowed when re-declared (meaning existing references to the old value won't change), however ref cells provide you with an object that you can mutate the "inner memory" of. They similar to SML's references.

Here's an example of a counter function that returns a value it increments internally every time we call it:

> def make_counter(start) =
  let
    curr = ref!(start)
  in
    fn () = map!(curr, +(1))
: Int -> (Unit -> Int)
> def counter = make_counter(0)
: Unit -> Int

> counter()
= 1 : Int

> counter()
= 2 : Int

> counter()
= 3 : Int

There are a few other features implemented in Bisquit, and a few things that are not working as I want them to yet, but the inference is in a good enough place for a pet project like this one so this project likely won't see much more work in the future.

About

Bisquit is an exercise in building a statically typed functional programming language with type inference.

Resources

Stars

Watchers

Forks

Languages