Skip to content

Commit

Permalink
update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
mental32 committed Oct 2, 2023
1 parent ea2110c commit c60b446
Showing 1 changed file with 51 additions and 159 deletions.
210 changes: 51 additions & 159 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,46 @@
<h1 align="center">Monty</h1>

<h1 align="center">A compiler for strongly typed Python.</h1>
<h1 align="center">A language toolchain for explicitly typed annotated Python.</h1>

## Index

- [Index](#index)
- [Brief](#brief)
- [How does Monty differ from regular Python or the typing semantics of other type checkers?](#how-does-monty-differ-from-regular-python-or-the-typing-semantics-of-other-type-checkers)
- [Abstract](#abstract)
- [Building the compiler](#building-the-compiler)
- [Crate/Repository Layout](#craterepository-layout)
- [Related projects](#related-projects)
- ["prior art"](#prior-art)

## Brief
## Abstract

Monty `(/ˈmɒntɪ/)` is a gradually typed, statically compilable Python.
With some baked in tricks to make it feel as dynamic as regular Python.
Monty `(/ˈmɒntɪ/)` is a specialized compiler designed to transpile Python 3.8+
code into a strongly typed, statically compiled language. The compiler aims to
maintain the inherent dynamism of Python while incorporating advanced type
inference and type-checking capabilities. Key Differentiators:

<!-- ### How does Monty differ from regular Python or the typing semantics of other type checkers?
1. Compile-Time Execution of Global Scope: Monty evaluates code in the global
scope at compile-time, thereby making the import process static. This
architectural decision is intended to encourage the segregation of business
logic from initialization code.

1. The most notable difference is that code in the global scope gets run at compile time instead of at program startup.
2. Gradual Code Porting: Monty is designed to parse and evaluate any Python
code, although it may selectively compile based on type safety and other
factors. This feature facilitates the gradual transition of existing Python
codebases.

This is done for several reasons however the most important one is generally to make importing a static (at-compile-time)
process rather than a lazy (at-program-startup-time) task.
3. Advanced Typing Semantics: Monty's type system is influenced by prominent
type checkers such as pytype, pyright, and pyre. It employs advanced type
inference algorithms and supports a variety of type-narrowing techniques.

The attitude monty has towards code is that all the business logic should be tucked away neatly organized behind
classes and functions, anything that is in the module/global scope should only be there to initialize and define
those classes and functions.
Upcoming Redesign:

The compile-time runtime is bounded so that programs may not hog time or infinitely execute. I/O is also restricted
and sandboxed by default prompting the terminal when code attempts to open files, bind or connect sockets, and read
input.
Monty is currently undergoing a significant redesign to become an LSP-first
compiler, emphasizing high responsiveness and extraordinarily fast compile
times.

1. Monty by default will accept (parse, comptime eval) **any** Python code. It may not, however, compile it all.
In the interest of making it easy to gradually port existing Python code so that monty can compile it: the compiler will parse
all modern Python (3.8+) code and it will submit the code through compile-time evaluation. this means that it is completely legal
to have Python code which monty cant compile (e.g. async/await, macros, etc...) alongside code that monty can compile.
It is a compilation error if monty discovers a call into code that it can not compile. All the extra code that isn't compilable is
still parsed, evaluated, and managed internally to make it usable for compile-time evaluation, or third-party analysis.
3. Monty typing takes after pytype, pyright, and pyre.
Monty takes direct inspiration from the three mainstream checkers: [pytype] (google), [pyright] (microsoft), [pyre] (facebook).
Like pytype: monty relies [heavily on inference and is lenient instead of strict](https://github.com/google/pytype#how-is-pytype-different-from-other-type-checkers)
Monty learnt about narrowing and guards from pyright [and supports many of the same guard and narrowing patterns](https://github.com/microsoft/pyright/blob/main/docs/type-concepts.md#type-narrowing)
A lot of existing code in monty is designed to be embeddable and query-able [similar to pyre](https://pyre-check.org/docs/querying-pyre/). -->
This redesign aims to make Monty not only a robust option for those seeking
strong typing in Python but also a highly efficient tool for large-scale and
real-time coding environments.

## Building the compiler

Expand All @@ -59,138 +50,39 @@ After that it's as simple as running: `cargo run --bin montyc -- --help`
### Crate/Repository Layout

* `/montyc` is the compiler binary, it is a thin wrapper around `montyc_driver`
* `/montyc_driver` is where all the magic happens, type checking/inference, calls into codegen, etc...
* `/montyc_codegen` is where codegen providers are, currently only Cranelift is supported but I'd like to support both LLVM and GCC in the future.
* `/montyc_hlirt` is a High Level Interpreter Runtime (HLIRT) and is a minimal but geniune Python interpreter used mainly for compile time evaluation.
* `/montyc_query` is where the query interface is defined for the driver.
* `/montyc_flatcode` is where AST -> FlatCode lowering happens.
* `/montyc_parser` is the parser implementation.
* `/montyc_core` is where all fundamental types used in this project go to live.

<!--
## What Monty can do to feel dynamic.
This section is a work in progress and it documents a few ideas
that I'm exploring to see if I can remove the typical hassle of
working with a strongly-typed, compiled language.
### "automatic unionization"
In Monty variables may only have one type per scope.
you may not re-assign a value to a variable with a different type.
```py
def badly_typed():
this = 1
this = "foo"
```
You may however have a union of types, which is internally represented like a tagged
union in C or an enum in Rust.
`typing.Union[T, ...]` is the traditional way to annotate a union explicitly but in
Monty you may use the newer literal syntax `T | U` from [PEP604]:
```py
def foo():
this: int | str = 1
this = "foo"
```
```py
def bar() -> int | bool:
if random.randrange(0, 2):
return 1
else:
return False
```
```py
def baz(qux: str | list[str]) -> int | bool:
...
```
And it even works with inference:
```py
def foo() -> int:
return 1
def bar() -> str:
return "foo"
def baz(control: bool):
x = foo() if control else bar()
```
Here the type of `x` in `baz` is inferred to be `Union[int, str]` depending on
the value of `control`.
### "Type narrowing"
Type narrowing [is not a new concept][type-narrowing] and its been around for a while in typecheckers.
The idea is, roughly, that you can take a union type and dissasemble it into one of its
variants through a type guard like:
```py
x: int | str | list[str]
if isinstance(x, int):
# x is now considered an integer in this branch of the if statement
elif isinstance(x, str):
# x is now considered a string here.
else:
# exhaustive-ness checks will allow `x` to be treated as a list of strings here.
```
### Staged computation of module-level code (aka "comptime"/"consteval")
The biggest difference between regular Python and Monty is how the module-level
is evaluated.
Python is lazy and everything gets run when its accessed, a
modules scope is still a big block of executable code after all and can be treated
as a function that operates on an implicit module object.
Monty treats a module's global scope as a big pool of constant declarations.
but this doesn't translate well for obvious reasons with already existing code
and semantics. To bridge this gap montyc has within itself a small AST-based
interpreter that is used to execute the code within a modules global scope.
Assuming most global-scope level logic is there to act as a sort of
"initializing glue routine" then the user can do whatever they like as long as:
* The execution finishes within a known amount of "ticks" (so that we don't accidentally run off into an infinite loop that never finishes.)
* The state of the module's global scope is semantically correct (the typechecker will verify the module after comptime execution has finished for a module.)
Of course in a completely dynamic environment we don't have to restrict the user
like we would when compiling the code regularly, so in that case most things that
would be rejected normally are perfectly fine such as: `exec`, `eval`,
`globals`, `locals`, dynamic class creation, and functions with untyped arguments. -->
* `/montyc_driver` is where all the magic happens, type checking/inference,
calls into codegen, etc... * `/montyc_codegen` is where codegen providers are,
currently only Cranelift is supported but I'd like to support both LLVM and GCC
in the future. * `/montyc_hlirt` is a High Level Interpreter Runtime (HLIRT)
and is a minimal but geniune Python interpreter used mainly for compile time
evaluation. * `/montyc_query` is where the query interface is defined for the
driver. * `/montyc_flatcode` is where AST -> FlatCode lowering happens. *
`/montyc_parser` is the parser implementation. * `/montyc_core` is where all
fundamental types used in this project go to live.

## Related projects

### ["prior art"](https://github.com/rust-lang/rfcs/blob/master/text/2333-prior-art.md)

- [Cython](https://github.com/cython/cython)
- [Numba](https://github.com/numba/numba)
- [Nuitka](https://github.com/Nuitka/Nuitka)
- [Peggen](https://github.com/gvanrossum/pegen)
- [MyPy](https://github.com/python/mypy)
- [PyPy](https://foss.heptapod.net/pypy/pypy)
- [RPython](https://foss.heptapod.net/pypy/pypy/-/tree/branch/default/rpython)
- [RustPython](https://github.com/RustPython/RustPython)
- [Pyston](https://github.com/pyston/pyston)
- [Pyjion](https://github.com/tonybaloney/Pyjion)
- [ShedSkin](https://github.com/shedskin/shedskin)
- [IronPython](https://github.com/IronLanguages/ironpython3)
### ["prior
art"](https://github.com/rust-lang/rfcs/blob/master/text/2333-prior-art.md)

- [Cython](https://github.com/cython/cython) -
[Numba](https://github.com/numba/numba) -
[Nuitka](https://github.com/Nuitka/Nuitka) -
[Peggen](https://github.com/gvanrossum/pegen) -
[MyPy](https://github.com/python/mypy) -
[PyPy](https://foss.heptapod.net/pypy/pypy) -
[RPython](https://foss.heptapod.net/pypy/pypy/-/tree/branch/default/rpython) -
[RustPython](https://github.com/RustPython/RustPython) -
[Pyston](https://github.com/pyston/pyston) -
[Pyjion](https://github.com/tonybaloney/Pyjion) -
[ShedSkin](https://github.com/shedskin/shedskin) -
[IronPython](https://github.com/IronLanguages/ironpython3)

[cranelift]: https://github.com/bytecodealliance/wasmtime/tree/main/cranelift
[llvm]: https://llvm.org/

[PEP604]: https://www.python.org/dev/peps/pep-0604/

[rpython-instances]: https://rpython.readthedocs.io/en/latest/translation.html#user-defined-classes-and-instances
[rpython-instances]:
https://rpython.readthedocs.io/en/latest/translation.html#user-defined-classes-and-instances
[type-narrowing]: https://www.python.org/dev/peps/pep-0647/#id3

0 comments on commit c60b446

Please sign in to comment.