# purescript-python
## Motivation
PureScript(especially PureScript-python) for what?
- Simple and intuitive Python interop
-
Advanced type system
- higher kinded types
- higer rank types
- functional dependencies
- extensible records
- data kinds
- etc..
This is an extreme of being pragmatic, and makes decoupling and composition easier.
- Excellent IDE, better type-driven programming experience, less of mental burden.
You understand what does
"implicit type inference + auto-completion + real-time error highlighting + type constraints by advanced type system" mean?
- Multiple back ends: JavaScript, Go/C++, Python, Kotlin, etc.
## Status
Currently many purescript libraries are supported, like
- prelude
- generics-rep(for `deriving` type classes like `Show`, `Generic`, etc.)
- console
- effect
- enums
- controls
- partial
- etc.
purescript-python has grown up to some degree with pretty nice availability.
## Python Package Generation Specification
Generating PySExpr
-------------------------
After slightly modifying a JavaScript-like IR produced by the builtin compiler,
PureScript gets compiled to [`PySExpr`](https://github.com/thautwarm/PySExpr) and **shall work since Python 3.5**.
The reason why we generate the IR `PySExpr` instead of Python source code,
is for getting better cross-Python-version compatibility, Python-version-specific optimizations,
**source code positioning for using existing Python debuggers in PureScript**, and expression-first expressiveness. You could check out [this reddit post](https://www.reddit.com/r/ProgrammingLanguages/comments/f41odv/a_compiler_back_end_by_which_you_write) for more details.
Directory Tree of Generated Package
---------------------------------------------
Given a PureScript module, not losing the generality, we abbreviate it as `A.B`.
After processing this module via the command
```shell
# `output` is the directory produced by the PureScript build tool `spago`.
pspy-blueprint --out-python aaa/bbb/output_top_dir --corefn-entry A.B --out-ffi-dep ffi-requires.txt
```
Command `pspy-blueprint` generates following directory tree(all `__init__.py` will be added later, but not in Haskell side):
```
- aaa/bbb/output_top_dir
- A
- B
- pure.py
- pure.zip.py
- (optional) pure.raw.py
- ffi
- ffi-requires.txt # lines of paths from which FFI files are required
```
`pure.raw.py` or `pure.zip.py` Generated for Each Module
-----------------------------------------------
This Python module creates Python code/bytecode object.
In CPython, every Python file will be compiled to a Python code object, which will finally be executed in
CPython virtual machine.
In the earlier design, we create the code object in `pure.raw.py`,
but don't execute it, for achieving the further flexibility of caching and composition of our compilation.
Unfortunately, due to the heavy code generation by PureScript's type-level computation, the generated `pure.raw.py` can be always very huge and cause a `MemoryError` when you want to import it as a python module.
To address this, we come up with a data file format [topdown](https://github.com/purescript-python/purescript-python/blob/master/src/Topdown/Topdown.hs) and use it to generate `pure.zip.py`, which is actually a `zip` file and shall be regarded as a compressed version of `pure.raw.py`, but also parse faster than a regular Python module. Sometimes, `pure.raw.py` can be more than 300MB,
which certainly crash any `python` executable, but equivalent `pure.zip.py` can be only 50KB, with orders-of-magnitude speed up on parsing.
`pure.py` Generated for Each Module
----------------------------------------------
This is, actually the loader for corresponding `pure.zip.py`/`pure.raw.py`.
This module implements the concrete code caching system which avoids the redundant Python source code to bytecode compilation, and finally greatly reduce the module loading time.
Hence, a PureScript module `A.B` compiled by PureScript-Python
will be able to imported by the statement `import output_top_dir.A.B.pure`.
The code of `pure.py`, corresponding to a PureScript module, is fixed to be
```python
from purescripto import LoadPureScript
__py__ = globals()
__ps__ = LoadPureScript(__file__, __name__)
__all__ = list(__ps__)
__py__.update(__ps__)
```
which relies on the Python package `purescripto`.
The Python package `purescripto` provides a common RTS and supplements all required functionalities for being a full featured PureScript backend.