Gabriella439 / Gabriella439/Haskell-Morte-Library
Ways to contribute to Morte
- Dominant language
- Haskell
- Stars
- 384
- Forks
- 25
- PR merge metrics
- No merged PRs in 30d
Description
Some people have asked me about some ways to contribute so I'm listing them here in case other people are interested in contributing to Morte, too.
The main ways to contribute are:
- [ ] Add a test suite
- [x] Add a benchmark suite
- [ ] Create a Haskell or LLVM backend
- [ ] Create a Haskell-like front-end
- [ ] Improve performance of normalization and type-checking
Here is a big dump of some of my thoughts on each specific point:
# Add a test suite
A good starting point would be to create one test per example in `Morte.Tutorial` and then some quick-check examples that verify things like:
- `normalize` is idempotent
- If an expression type-checks, then the normalized form still type-checks
Quickcheck tests used to be harder to do for Morte because of the issue of how to generate variable names, but now that Morte supports DeBruijn indices this is easier.
# Add a benchmark suite
A good starting point is the `recursive.mt` example from the tutorial, which is expensive to normalize (almost a second on my machine)
# Create a Haskell backend
The simplest approach is to treat Morte as essentially a subset of Haskell (if you ignore the dependent types functionality) and you can compile Morte to a single Haskell expression that can either:
a) be embedded within a Haskell program, or:
b) compiled to an executable (if the expression has type `IO ()`)
The non-trivial parts are:
- Parametrizing `typeOf`/`typeWith` on `rules`:
The reason why is that you need type-check the final normalized expression with [this rule](https://github.com/Gabriel439/Haskell-Morte-Library/blob/master/src/Morte/Core.hs#L175) deleted in order to enforce that the expression you compile can be translated to Haskell. That particular rule needs to be deleted because it's the one that permits dependent types, which Haskell does not fully support. It's okay if the initial input expression has dependent types, but they have to disappear by the time the expression is normalized.
- Creating a Haskell FFI
Normally this would just involve adding every FFI-imported type/function/literal as just another lambda-bound value. Here are some examples:
```
-- FFI types
\(U : *) -- `()`, the type
-> \(Unit : U) -- `()`, the data
-> \(String : *)
-> \(Int : *)
-> \(IO : * -> *)
-- FFI functions
-> \((>>=) : forall (a : *) -> forall (b : *) -> IO a -> (a -> IO b) -> IO b)
-> \(return : forall (a : *) -> a -> IO a)
-> \(putStrLn : String -> IO U)
-> \(getLine : IO String)
-> \((+) : Int -> Int -> Int)
-> \((*) : Int -> Int -> Int)
-- FFI values/literals
-> \(int0 : Int)
-> \(int1 : Int)
-> \(str0 : String)
-> \(str1 : String)
-> ...
```
The mapping between Morte's lambda-bound types/functions/literals and their equivalent Haskell names needs to be supplied out-of-band.
# Create an LLVM backend
All the challenges of the above Haskell backend apply plus the additional challenge of compiling a functional language to LLVM. I'm not an expert on this so I don't know how difficult it would be.
# Create a Haskell-like front-end
I'm already working on one such front-end here:
https://github.com/Gabriel439/Haskell-Annah-Library
It's still very rough and incomplete. Any other front-ends are also welcome.
# Improve performance of normalization and type-checking
One of the ways you can improve the performance of Morte is to just add two fields to every constructor of the `Expr` type: one for the normalized form of the expression and one for the inferred type. If you instantiated these fields lazily then they could be cached and reused in order to avoid duplicate computation.
This would be a more invasive change to the compiler.
Contributor guide
No contributing guide indexed for this repository
Research direction
Choose one unchecked contribution area before starting, since this issue combines tests, backends, front-ends, and compiler performance work. For tests, begin with the examples in Morte.Tutorial; for backend work, read src/Morte/Core.hs and the type-checking rule mentioned there; for benchmarking, use recursive.mt. Done requires a separately defined scope and validation for the selected area.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- haskell
- Domain
- compilers, performance, testing-qa
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 18/100