A statically typed, functional language inspired by Haskell, ML, and Rust. This language is effectively a rewrite and redesign of all the prior languages I've (incompletely) implemented, but with an emphasis on modular (and ideally non-monolithic) code.
The Wysk language aims to eventually branch away from its admittedly Haskell-heavy influence, and currently touts the following features (either complete or as goals):
- algebraic data types
- static type inference via Hindley-Milner
- systematic overloading and polymorphism via type classes
- a flexible module system
- robust concurrency afforded by lazy semantics and a run-time system written purely in Rust
- leveraging the low-level power of Rust with the high-level semantics of Haskell
I am still fairly inexperienced when it comes to compiler development, so it follows that this project -- and its documentation -- is very much a work in progress.
Additionally, this compiler aims to use as little dependencies as possible.
The common exceptions to this are the lazy_static
, serde
and toml
crates;
aside from these two, the lack of external dependencies allows for a greater
amount of flexibility and control over specific functions and implementations
used within the compiler, as well as proving to be a wonderful exercise in
learning how to truly appreciate what the Rust standard library has to offer.
With that being said, additional dependencies will likely be added on an
as-needed basis as the compiler itself matures -- but that'll have to wait until
I get tired of handrolling my own Rust :).
The entry point to every program, the function main
operates within IO
. The
actual return type of main
is generally irrelevant, but must be contained
within the IO
type.
fn main :: IO ()
= printLine "Hello world!"
Wysk does not have traditional loops; instead it relies on recursion to
achieve the same effect. With tail-call optimization, this generally allows
for fearless recursion (assuming convergent tail recursion). This can be
exploited along with case
-like syntax at the function definition level,
allowing for branches to be predicated upon from a function's top-most scope.
fn factorial :: Int -> Int
| n if n < 2 = 1
| n = n * factorial (n - 1)
Functions may be matched on with either case
or match
expressions, where
match
expressions correspond to function
in OCaml or \case
in Haskell,
i.e., are sugar for \x -> match x { ... }
. Wysk additionally supports
(nested) or-patterns, allowing for pattern matching to be written as concisely as
necessary without repeated code.
fn fibs :: Int -> Int
= match
| (0 | 1) -> 1
| n -> fibs (n - 1) + fibs (n - 2)
The following may not necessarily be directly involved within the development of this compiler, but have proven nonetheless to be valuable sources of information.
- [1987] Simon Peton Jones. The Implementation of Functional Programming Languages
- [1992] Simon Peyton Jones. Implementing lazy functional languages on stock hardware: the Spineless Tagless G-machine
- [1997] Simon Peyton Jones, Jones & Meijer. Type classes: an exploration of the design space
- [1999] Simon Peyton Jones, Simon Marlow. Secrets of the Glasgow Haskell Compiler inliner
- [2000] Mark P. Jones, Typing Haskell in Haskell
- [2002] Paul Graham, The Roots of Lisp
- [2004] Luca Cardelli, Type Systems
- [2005] Daan Leijen. Extensible records with scoped labels
- [2010] The Haskell Community. The Haskell 2010 report
- [2011] Vytiniotis et al. OutsideIn(X): Modular type inference with local assumptions
- [2013] Susan B. Horwitz. UW CS704 notes
- [2013] Dunfield and Krishnaswami. Complete and easy bidirectional typechecking for higher-rank polymorphism
- [2015] Stephen Diehl. Write you a Haskell
- [2020] Serrano, Have, Jones & Vytiniotis. A Quick Look at Impredicativity
- [2022] Gonglin Li. An Affine Type System with Hindley-Milner Style Type Inference