The programming language for people who want Python's simplicity without Python's ecosystem sprawl.
Fly is a high-level, dynamically typed, general-purpose programming language designed for native compilation, practical software development, and a complete first-party developer toolchain.
Write readable code. Compile it to a native executable. Manage dependencies. Build projects. Run tests. Format code. Package software. Update the toolchain.
One language. One toolchain. One ecosystem.
Programming languages often make you choose.
You can have a friendly language, but then end up juggling a compiler, package manager, build system, environment manager, formatter, test runner, dependency files, and third-party tools.
Or you can use a powerful systems language and spend more time fighting the language than building your software.
Fly is designed around a different idea:
The language and the toolchain should feel like one product.
Fly takes the approachable style of high-level scripting languages and combines it with a native compiler and first-party project tooling.
- A readable, high-level syntax
- Dynamic typing
- Native executable compilation
- A built-in project toolchain
- Dependency management
- A package ecosystem
- Project manifests
- Formatting
- Testing
- Cleaning build artifacts
- Running projects
- Toolchain updates
- Module support
- A native REPL
- Cross-platform compiler architecture
- Integer bitwise operators (
band,bor,bxor,bnot,shl,shr) - A distinctive, simple vocabulary
name = take("What is your name? ")
if name == "Rick" {
show("Welcome back, {name}!")
}
orif name == "admin" {
show("Administrator access granted.")
}
ifnot {
show("Hello, {name}!")
}
Functions are deliberately simple:
job add(a, b) {
give a + b
}
result = add(10, 20)
show("Result: {result}")
And because Fly is dynamically typed:
value = 42
value = "Hello"
value = Yes
No type declaration ceremony is required.
Fly is a compiled language.
A .fly source file is transformed through the Fly compiler into a native executable.
.fly
↓
Lexer
↓
Parser
↓
Semantic analysis
↓
LLVM IR generation
↓
LLVM optimization
↓
Native backend
↓
Linker
↓
Executable
Fly does not require a language-level interpreter or virtual machine for normal execution.
The compiler is implemented in C++ and uses LLVM for native code generation.
The runtime is implemented separately as libflyrt with a stable C ABI.
Fly is more than a compiler.
The fly command is the entry point for the development workflow.
fly
├── compile
├── build
├── run
├── test
├── deps
├── dump
├── new
├── format
├── clean
├── up
└── uninstall
Compile one Fly source file:
fly -compile src/main.flyAn icon can be supplied for Windows native executables:
fly -compile src/main.fly -icon logo.icoBuild a Fly project from its flylink.sleep manifest:
fly -buildThe project manifest describes the source file, output and dependencies.
Example:
project
name "hello"
version "0.1.6"
source "src/main.fly"
output "bin/hello"
icon "assets/hello.ico"
deps coll ["http"]
Build and run the project:
fly -runFly can determine when a rebuild is necessary from project source and module timestamps.
Run the project's tests:
fly -testFormat Fly source using the official formatter:
fly -formatThe goal is to avoid every project inventing its own formatting conventions.
Remove generated build artifacts:
fly -cleanCreate a new Fly project:
fly -new myappfly -init myapp remains available as a compatibility alias.
A project contains a flylink.sleep manifest and a source tree.
Typical layout:
myapp/
├── flylink.sleep
├── src/
│ └── main.fly
├── module/
└── bin/
Fly is designed around an unusually integrated development experience.
Instead of requiring a collection of unrelated tools:
language
compiler
build system
package manager
dependency file
formatter
test runner
project generator
runtime
Fly brings these pieces together under the same toolchain.
The goal is simple:
You should be able to install Fly and have everything you need to start building software.
That does not mean Fly prevents developers from using external tools.
It means they should not be mandatory merely to get a normal project built and managed.
Fly projects use the deps field in flylink.sleep.
project
name "webapp"
version "0.1.6"
source "src/main.fly"
output "bin/webapp"
deps coll ["http", "sleep"]
Dependencies are managed through Fly's package ecosystem.
Install a package:
fly -dump install httpList installed packages:
fly -dump listRemove a package:
fly -dump remove httpUpdate packages:
fly -dump updateOffline dependency operations can be performed with:
fly -dump install http --offlineThe package system is designed around the Fly ecosystem rather than requiring a separate package manager.
Dump is Fly's package index and distribution mechanism.
Public packages are published through the Fly Dump ecosystem.
This allows Fly projects to use normal module imports:
bring http
bring sleep
while the project toolchain handles obtaining those dependencies.
The project manifest records dependencies so builds remain reproducible and understandable.
Fly uses SLEEP for its project manifest format.
Example:
project
name "myapp"
version "0.1.6"
source "src/main.fly"
output "bin/myapp"
icon "assets/myapp.ico"
deps coll ["http"]
SLEEP stands for:
Simple Lightweight Extensible Expression Protocol
It is an indentation-based data/configuration format designed to be easier to write and read than heavily punctuated configuration formats.
Fly uses SLEEP because project configuration should be readable too.
The exact accepted SLEEP grammar is defined by the SLEEP parser and project loader used by the toolchain.
Fly projects can specify an executable icon:
project
name "myapp"
version "0.1.6"
source "src/main.fly"
output "bin/myapp"
icon "assets/myapp.ico"
deps coll []
When building a Windows executable, the configured icon can be embedded into the resulting PE executable.
For one-off compilation:
fly -compile src/main.fly -icon myapp.icoThe intended flow is:
flylink.sleep
↓
project icon
↓
fly -build
↓
fly-cc
↓
native executable
↓
embedded Windows icon
Fly is dynamically typed.
name = "Rick"
age = 18
pi = 3.14
ready = Yes
Variables do not normally require explicit type declarations.
Fly 0.1.6 defines these core types::
| Type | Description | Example |
|---|---|---|
tex |
Text | "Hello" |
num |
Integer number | 42 |
dec |
Decimal number | 3.14 |
yn |
Boolean | Yes |
coll |
Collection | [1, 2, 3] |
board |
Key/value collection | {"name": "Rick"} |
emp |
Empty value | EMP |
Types describe Fly values but normally do not appear in variable declarations.
Variables are mutable by default.
a = 10
a = 20
Fly is dynamically typed, so the same variable can hold different types:
a = 10
a = "Hello"
a = Yes
Use hard for an immutable variable:
hard name = "Rick"
After initialization:
hard age = 18
age = 19
The reassignment is an error.
hard applies to all Fly value types.
Single-line comments begin with $.
$ This is a comment
name = "Rick" $ Inline comment
Multiline comments use $$:
$$
This is a multiline comment.
It can span multiple lines.
$$
Fly uses:
Yes
No
Example:
ready = Yes
running = No
Fly uses:
EMP
for an empty or absent value.
value = EMP
Operations that cannot produce a meaningful value may return EMP.
For example:
position = seek(items, "Nobody")
if position == EMP {
show("Not found.")
}
+
-
*
/
%
Example:
a = 10 + 5
b = 20 * 4
==
!=
<
>
<=
>=
and
or
not
Example:
ready = Yes and running == No
Text values use double quotes:
name = "Rick"
message = "Hello!"
Fly supports expression interpolation directly inside text:
age = 17
show("Next year you will be {age + 1}")
Expressions may be arbitrary Fly expressions:
job add(a, b) {
give a + b
}
show("Result: {add(10, 5)}")
Direct values also work:
show("Value: {age}")
Expressions are evaluated when the text value is created.
A literal { is written as:
{{
A literal } is written as:
}}
Example:
show("Use {{Name}} as an example.")
produces:
Use {Name} as an example.
Collections are ordered sequences.
items = [10, "Hello", Yes, 3.14]
Collections can contain mixed types.
Indexing starts at zero:
items = ["A", "B", "C"]
show(items[0])
Output:
A
Collections and text support square-bracket indexing:
items[0]
name[1]
Text indexing produces a single text element.
Collections and text support slicing:
items[1:4]
name[0:3]
The starting index is inclusive.
The ending index is exclusive.
Example:
name = "RICK"
show(name[1:4])
Output:
ICK
Omitted boundaries are supported:
name[:3]
name[2:]
name[:]
Fly uses readable command-oriented names for common operations.
Append a value:
attach(items, "New")
Insert at an index:
place(items, 1, "Inserted")
Remove an element:
erase(items, 2)
Get the number of elements:
count(items)
Objects that expose a length API also support:
items.length()
name.length()
Search for a value or substring:
seek(items, "Alex")
seek("Hello World", "World")
For collections, seek returns the matching index.
For text, seek returns the matching substring position.
When no match exists:
EMP
Check for a value or substring:
has(items, "Alex")
has("Hello World", "World")
The result is:
Yes
No
Join collection values into text:
items = ["A", "B", "C"]
result = bind(items, ", ")
Result:
A, B, C
Split text into a collection:
sever("A,B,C", ",")
Result:
["A", "B", "C"]
Remove surrounding whitespace:
cut(text)
Convert text to uppercase:
raise(text)
Convert text to lowercase:
lower(text)
A board stores key/value pairs.
Example:
people = {
"Rick": 18
"Bob": 25
}
Boards may use non-text keys.
The exact board literal grammar and access semantics are part of the Fly grammar/toolchain specification.
Functions are declared with job:
job add(a, b) {
give a + b
}
Call them normally:
result = add(10, 20)
Functions do not require parameter or return-type declarations.
Use give:
job square(x) {
give x * x
}
A function that finishes without giving a value produces:
EMP
Fly 0.1.6 provides a minimal group object model: group declares a type with fields and methods, and calling the group's name like a function constructs an instance.
group Person {
name
age
job introduce() {
show("hello, i'm " + self.name + " (age " + tex(self.age) + ")")
}
job is_old() {
give self.age > 50
}
}
group Name { ... }declares a group.- Inside the block, a bare identifier (
name,age) declares an instance field. - A
jobinside the block declares a method. Methods use the ordinaryjobsyntax (job name(params) { ... }) and are not callable as flat top-level jobs. - Inside a method,
selfis the current instance.self.fieldreads a field;self.method(...)calls a method — including an inherited one.
Construct an instance by calling the group's name like a function:
bob = Person("bob", 8)
Arguments are assigned positionally to fields in declaration order (the first argument fills the first declared field, and so on). Every field not given an argument stays EMP.
There is no member/new constructor keyword and no constructor function — the group-name call is the object-construction syntax.
Instances support member access with . for both fields and methods:
alice.describe()
bob.introduce()
bob.age
bob.is_old()
show("bob old: {bob.is_old()}")
Method calls through instances (including inside interpolation) work as shown. A missing field or method is a runtime error, not a compile error.
Single inheritance is declared with from:
group Employee from Person {
role
job describe() {
self.introduce()
show("role: " + self.role)
}
}
- The child inherits the parent's fields and methods:
Employeehasname,age(fromPerson) androle(its own), in that order. - Inherited methods can be called directly:
self.introduce()insidedescribe()callsPerson's method. - A child method with the same name as a parent's overrides it for the child.
superis not implemented:super.method()is a compile error (undefined identifier 'super').
group Person {
name
age
job introduce() {
show("hello, i'm " + self.name + " (age " + tex(self.age) + ")")
}
job is_old() {
give self.age > 50
}
}
group Employee from Person {
role
job describe() {
self.introduce()
show("role: " + self.role)
}
}
alice = Employee("alice", 55, "sr eng")
bob = Person("bob", 8)
alice.describe()
bob.introduce()
show("bob old: {bob.is_old()}")
show("alice old: {alice.is_old()}")
Output:
hello, i'm alice (age 55)
role: sr eng
hello, i'm bob (age 8)
bob old: No
alice old: Yes
This is the oop_receiver regression fixture (tests/e2e/oop_receiver.expected).
The current 0.1.6 group model is deliberately minimal. Not implemented:
member,super,child,of,at,new— none of these is a keyword (they lex as ordinary identifiers). Construction is the group-name call described above.super.method()parent dispatch.- Field assignment:
self.age = 3is not supported; fields can only be read withself.field. - Field defaults/initializers: fields always start as
EMP. - Constructor arity checking: extra arguments to a group call are silently ignored.
- Direct field names inside methods: a method must use
self.field, never the bare field name (a bare name is an undefined identifier). for ... inover instances (iteration requires a coll, board, tex, or bytes value).- Any group/method containment, instance type checks, or group fields/methods at non-top level.
- Objects are heap-allocated but not yet reference-counted (object memory is not reclaimed in this milestone), and
show()/interpolation renders an instance as<unknown fly value>.
Read input with take:
name = take("What is your name: ")
Display values with show:
show("Hello")
show(name)
show(10 + 5)
Fly uses:
if
orif
ifnot
Example:
name = take("What is your name? ")
if name == "admin" {
show("Welcome back!")
}
orif name == "RICK" {
show("Hi rick!")
}
ifnot {
show("Hello, {name}!")
}
Fly provides while and for.
while condition {
...
}
for item in items {
show(item)
}
Iteration semantics depend on the iterable value.
Fly uses:
skip
getout
skip skips the remainder of the current iteration.
getout exits the current loop.
wait pauses the program for a given duration:
wait 0.05
wait .5
wait 1
The argument must be a numeric value (num or dec) giving the number of seconds to sleep. A non-numeric argument is a runtime error:
type error: wait expects a numeric expression (num or dec)
Negative durations are clamped to 0 (no sleep). wait is a statement, not a function — it does not produce a value. On Windows it maps to Sleep, on POSIX to usleep.
Fly provides structured error handling through do and grabe.
do {
...
}
grabe (err) {
...
}
Example:
do {
value = num("hello")
}
grabe (err) {
show("The conversion failed.")
}
An error that is not handled by a surrounding grabe block propagates outward.
An unhandled error terminates the current operation/program and reports the error.
Fly is dynamically typed, but values can be explicitly converted between compatible types.
The target type acts as the conversion function:
age = num("18")
price = dec("19.99")
text = tex(123)
More examples:
a = num(10.8)
b = dec(10)
c = tex(10)
Invalid conversions produce runtime errors.
do {
number = num("hello")
}
grabe (err) {
show("Invalid number.")
}
Casting does not mutate another variable's original value.
Fly supports modules using bring:
bring path
bring filesystem
Imported modules provide functionality to the program.
Public modules can be installed through Fly's package ecosystem.
A public module can also be imported under an alias:
bring kounter as math
show(math.sqrt(9))
show(kounter.sqrt(4))
The alias is a source-level re-spelling of the module's qualified jobs.
The real module name remains valid.
The exact aliasing and collision rules are defined by the compiler implementation and tests.
Fly's toolchain defines compiler/runtime-backed modules including:
net
filesystem
process
environment
system
path
gui
These expose standard platform functionality through Fly's API.
Examples include:
path.join(...)
filesystem.read(...)
process.run(...)
environment.get(...)
system.os(...)
net.connect_tls(...)
gui.window(...)
The compiler recognizes the built-in API surface and routes these operations to the Fly runtime.
The gui module provides native windows/widgets where platform support is available.
Public ecosystem modules are separately distributed Fly source.
They can be installed into a project's module tree and imported with bring.
Example:
module/
└── http.fly
Then:
bring http
Some public modules are distributed as part of the Fly ecosystem, including:
sleep
http
hashbox
hashbox provides SHA-256 functionality such as:
bring hashbox
hashbox.sha256(text)
hashbox.file_sha256(path)
Public modules remain separate from compiler/runtime-backed functionality.
Fly's networking architecture supports native networking facilities.
The toolchain/runtime provides capabilities including:
- TCP sockets
- HTTPS/TLS
- DNS
- Platform networking APIs
HTTP functionality can be provided through public modules.
Example:
bring http
response = http.get_tls("https://google.com")
show(response["status"])
Native TLS support uses real TLS rather than silently falling back to plain HTTP.
Certificate and hostname verification are part of the secure HTTPS path.
Fly uses automatic reference counting (ARC).
Fly does not use a tracing garbage collector as its language runtime memory model.
The current runtime uses tagged FlyValue values with reference counting.
Reference counts are atomic where required by concurrent execution.
Cycles are a known limitation of the current model.
The goal is predictable native memory management while keeping the language high-level.
The runtime represents Fly values with a tagged value structure.
Conceptually:
FlyValue
├── NUM
├── DEC
├── YN
├── EMP
├── TEX
├── COLL
└── BOARD
The runtime uses a compact tagged representation and heap-backed structures for complex values such as text, collections and boards.
The Fly runtime is separated from the compiler.
fly-cc
│
└── native program
│
└── libflyrt
libflyrt is implemented in C and exposes a stable C ABI to generated code.
This separation allows the compiler and runtime to evolve independently.
The current compiler architecture is:
┌───────────────�
│ .fly file │
└───────┬───────┘
│
Lexer
│
Parser
│
Sema
│
IRGen
│
LLVM modules
│
LLVM linking
│
LLVM opt
│
LLVM backend
│
Linker
│
Native binary
Each source file can be represented as an LLVM module.
Modules can be linked at the LLVM IR level before optimization, allowing cross-file optimization opportunities such as inlining.
The Fly toolchain is divided into clear roles.
The main developer-facing tool.
It handles project and toolchain commands such as:
fly
fly -compile
fly -build
fly -run
fly -test
fly -deps
fly -dump
fly -new
fly -format
fly -clean
fly -about
fly -up
fly -uninstall
fly -init remains available as a compatibility alias for fly -new.
With no command-line flags, Fly launches the REPL.
The native Fly compiler.
It handles:
lexer
parser
semantic analysis
IR generation
LLVM
linking
native executable generation
The interactive Fly REPL.
The REPL uses the real Fly compiler rather than maintaining a completely separate interpreter implementation.
Conceptually:
REPL input
↓
Fly compiler
↓
native executable
↓
execute
This keeps REPL behavior close to actual compiled-program behavior.
Example:
>>> 1 + 1
2
>>> a = 10
>>> a
10
>>> "hello"
hello
>>> 10 * 4
40
>>> Yes
Yes
Explicit output calls are not duplicated:
>>> show(1 + 1)
2
Multiline expressions can continue across input lines:
>>> (1 +
... 1)
2
The REPL preserves successful session state while failed turns do not corrupt previously established state.
Fly is designed to update itself through the same toolchain.
The intended command is:
fly -upCurrently supported on Windows. The updater discovers releases from the repo's version.txt, builds the release URLs from the discovered version (never from a version baked into an older executable), verifies the release archive's SHA-256 against checksums.txt before replacing anything, replaces exactly fly.exe/fly-cc.exe/fly-repl.exe/libflyrt.a, and removes every temporary artifact.
The Windows distribution is intended to provide a normal installer experience:
fly-setup.exe
↓
Fly installer
↓
Fly installed
↓
PATH configured
↓
fly
The Windows installation includes the native Fly executables and required runtime components.
Fly can produce native Windows executables.
The Windows distribution includes the compiler, runtime components and required runtime DLLs.
Executable icons can be embedded into the PE executable rather than merely copied beside it.
This means a built Fly application can appear as a normal Windows application in Explorer and shortcuts.
Fly's compiler architecture is intended to be portable.
The high-level architecture separates:
language frontend
↓
LLVM IR
↓
platform backend/linker
↓
native executable
Platform-specific runtime functionality is isolated where necessary.
Windows and POSIX platforms may use different underlying system APIs while exposing the same Fly-level concepts.
A typical Fly project looks like:
myapp/
├── flylink.sleep
├── src/
│ ├── main.fly
│ └── utils.fly
├── module/
│ └── http.fly
├── assets/
│ └── myapp.ico
└── bin/
└── myapp.exe
A minimal manifest:
project
name "myapp"
version "0.1.6"
source "src/main.fly"
output "bin/myapp"
deps coll []
With an icon:
project
name "myapp"
version "0.1.6"
source "src/main.fly"
output "bin/myapp"
icon "assets/myapp.ico"
deps coll []
The intended Fly workflow is:
Install Fly
↓
fly -new myapp
↓
Write .fly files
↓
fly -dump install ...
↓
fly -build
↓
fly -run
↓
fly -test
↓
fly -format
↓
Ship native executable
No separate project generator, package manager, formatter or build-system configuration is required for the basic workflow.
Fly does not attempt to reproduce another programming language.
Its design priorities are:
- Readability
- Simplicity
- Consistency
- Expressiveness
- Practical native compilation
- A distinctive Fly vocabulary
The goal is not to make every feature configurable.
The goal is to make the common path pleasant.
Fly is intentionally comfortable for programmers familiar with high-level languages.
However, Fly is not Python syntax with a few renamed keywords.
Fly has its own:
- Syntax
- Types
- Runtime
- Compiler
- Module system
- Error handling model
- Package manager
- Project manifest
- Build system
- CLI
- Ecosystem
The point is to provide a similar level of accessibility while building a different language from the ground up.
This section summarizes the current core Fly language.
.fly
$ comment
$$
comment
comment
$$
tex
num
dec
yn
coll
board
emp
Yes
No
EMP
a = 10
Immutable:
hard a = 10
Arithmetic:
+
-
*
/
%
Comparison:
==
!=
<
>
<=
>=
Logical:
and
or
not
message = "Hello"
Interpolation:
show("Hello {name}")
Escaped braces:
show("Use {{name}} literally.")
items = [1, 2, 3]
Index:
items[0]
Slice:
items[1:3]
items[:3]
items[2:]
items[:]
person = {
"name": "Rick"
"age": 18
}
job add(a, b) {
give a + b
}
group Person {
name
age
job introduce() {
show("Hello, {self.name}!")
}
}
alice = Person("alice", 8)
alice.introduce()
group Name [from Parent] { field ... job method(...) { ... } } declares a group; calling the group name like a function constructs an instance, with arguments assigned positionally to fields (declaration order). self.field reads fields; self.method(...) and instance.method(...) call methods. group Child from Parent inherits the parent's fields and methods. super, member, field assignment, field defaults, and arity checks are not implemented.
name = take("Name: ")
show(name)
if condition {
...
}
orif other_condition {
...
}
ifnot {
...
}
while condition {
...
}
for item in items {
show(item)
}
skip
getout
wait 0.5
wait sleeps for a num or dec number of seconds (non-numeric values are runtime errors; negatives clamp to 0).
do {
...
}
grabe (err) {
...
}
num("42")
dec("3.14")
tex(123)
bring path
bring hashbox as hb
Fly 0.1.6 reserves:
if
orif
ifnot
while
for
job
give
take
show
bring
do
grabe
hard
skip
getout
group
from
wait
attach
place
erase
count
seek
has
bind
sever
cut
raise
lower
Yes
No
EMP
Compatibility aliases or legacy vocabulary may exist in development versions, but new code should use the current Fly 0.1.6 names.
Fly 0.1.6 — Language and toolchain milestone
Current development version: 0.1.6
Current implemented areas include:
- Dynamic typing
- Mutable variables
hardimmutable variablestexnumdecyncollboardemp- Arithmetic
- Comparison
- Logical operators
- Indexing
- Slicing
- String interpolation
- Functions
- Conditions
whileforskipgetout- Groups (objects and methods):
group, instances, fields, methods,self,fromsingle inheritance waitstatement- Input/output
- Collection operations
- Error handling
- Type casting
- Modules
- Module aliasing
- Native compilation
- LLVM backend
- Native runtime
- Project manifests
- Dependency management
- Package management
- Formatting
- Testing
- Project initialization
- Build/run/clean workflows
- Native Windows executables
- Windows installation tooling
- Executable icons
- GUI windows
- SHA-256 hashing through the
hashboxpublic module - Native REPL
- Toolchain update infrastructure
Some advanced APIs and platform capabilities continue to evolve.
Features explicitly marked as planned or proposed elsewhere in this document are not part of the implemented 0.1 language unless separately verified.
job greet(name) {
if name == EMP {
give "Nobody"
}
give name
}
name = take("What is your name? ")
message = greet(name)
show("Hello, {message}!")
items = ["Fly", "SLEEP", "Dump"]
attach(items, "LLVM")
for item in items {
show(item)
}
show("Items: {count(items)}")
input = take("Enter a number: ")
do {
number = num(input)
show("You entered {number}.")
}
grabe (err) {
show("That wasn't a valid number.")
}
bring http
response = http.get_tls("https://example.com")
show(response["status"])
flylink.sleep:
project
name "webapp"
version "0.1.6"
source "src/main.fly"
output "bin/webapp"
icon "assets/webapp.ico"
deps coll ["http"]
src/main.fly:
bring http
response = http.get_tls("https://example.com")
show("HTTP status: {response["status"]}")
Build:
fly -buildRun:
fly -runFly's philosophy is not that every feature must live in the language itself.
Instead:
The language should stay small while the ecosystem stays complete.
That means:
- language syntax stays readable
- fundamental capabilities live in the runtime or built-in APIs
- higher-level reusable functionality can live in public modules
- third-party functionality lives in packages
- the toolchain provides a consistent way to obtain and use dependencies
The result is intended to feel like one ecosystem rather than a pile of unrelated utilities.
Some capabilities required by more advanced software distribution and archive tooling are planned rather than implemented in the current 0.1 language.
Potential future work includes:
- Binary/byte data support
- Binary-safe filesystem I/O
- Binary-safe network I/O
- Native bitwise operators
- More complete binary manipulation primitives
- A public ZIP/archive module
- ZIP STORED support
- ZIP DEFLATE support
- ZIP INFLATE support
These features should be implemented at the correct architectural layer rather than simulated through undocumented or language-specific workarounds.
Fly 0.1.6 establishes the core language and toolchain.
Future work can expand:
- Binary and systems capabilities
- Standard libraries
- More public modules
- More platform targets
- Better diagnostics
- Faster builds
- Better optimization
- More packaging capabilities
- Broader testing infrastructure
- Stable module APIs
- More complete developer tooling
- A mature package ecosystem
The exact roadmap may change as implementation progresses.
Fly is a language and toolchain project.
Contributions can involve:
- The language frontend
- Compiler/code generation
- LLVM integration
- Runtime
- Built-in modules
- Public modules
- Package infrastructure
- Toolchain commands
- Documentation
- Tests
- Developer experience
When modifying Fly, preserve the distinction between:
language
compiler
runtime
built-in modules
public modules
toolchain
ecosystem
A feature should live in the layer where it naturally belongs.
Fly is licensed under the PolyForm Noncommercial License 1.0.0.
The complete license is provided in LICENSE.
The license file is the authoritative source for the permissions and restrictions governing Fly.
Write it simply. Compile it natively. Build everything with one toolchain.
Fly
│
┌─────────────┼─────────────�
│ │ │
Language Compiler Runtime
│ │ │
└─────────────┼─────────────┘
│
Toolchain
│
┌────────────┼────────────�
│ │ │
Build Packages Run
│ │ │
└────────────┼────────────┘
│
Ecosystem
│
Software
One language. One toolchain. One ecosystem.