Skip to content

Repository files navigation

Fly

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.


Why Fly?

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.

Fly aims to give you

  • 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

A Fly program

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.


Native compilation

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.


The Fly toolchain

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

Compile one Fly source file:

fly -compile src/main.fly

An icon can be supplied for Windows native executables:

fly -compile src/main.fly -icon logo.ico

Build

Build a Fly project from its flylink.sleep manifest:

fly -build

The 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"]

Run

Build and run the project:

fly -run

Fly can determine when a rebuild is necessary from project source and module timestamps.


Test

Run the project's tests:

fly -test

Format

Format Fly source using the official formatter:

fly -format

The goal is to avoid every project inventing its own formatting conventions.


Clean

Remove generated build artifacts:

fly -clean

Initialize a project

Create a new Fly project:

fly -new myapp

fly -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/

One ecosystem

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.


Packages and dependencies

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 http

List installed packages:

fly -dump list

Remove a package:

fly -dump remove http

Update packages:

fly -dump update

Offline dependency operations can be performed with:

fly -dump install http --offline

The package system is designed around the Fly ecosystem rather than requiring a separate package manager.


Dump

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.


flylink.sleep

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.


Project icons

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.ico

The intended flow is:

flylink.sleep
      ↓
project icon
      ↓
fly -build
      ↓
fly-cc
      ↓
native executable
      ↓
embedded Windows icon

Fly language

Dynamic typing

Fly is dynamically typed.

name = "Rick"
age = 18
pi = 3.14
ready = Yes

Variables do not normally require explicit type declarations.


Values and types

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

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

Immutable variables

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.


Comments

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.
$$

Booleans

Fly uses:

Yes
No

Example:

ready = Yes
running = No

Empty values

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.")
}

Operators

Arithmetic

+
-
*
/
%

Example:

a = 10 + 5
b = 20 * 4

Comparison

==
!=
<
>
<=
>=

Logical

and
or
not

Example:

ready = Yes and running == No

Text

Text values use double quotes:

name = "Rick"
message = "Hello!"

String interpolation

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.


Escaped braces

A literal { is written as:

{{

A literal } is written as:

}}

Example:

show("Use {{Name}} as an example.")

produces:

Use {Name} as an example.

Collections

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

Indexing

Collections and text support square-bracket indexing:

items[0]
name[1]

Text indexing produces a single text element.


Slicing

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[:]

Collection and text operations

Fly uses readable command-oriented names for common operations.

attach

Append a value:

attach(items, "New")

place

Insert at an index:

place(items, 1, "Inserted")

erase

Remove an element:

erase(items, 2)

count

Get the number of elements:

count(items)

Objects that expose a length API also support:

items.length()
name.length()

seek

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

has

Check for a value or substring:

has(items, "Alex")
has("Hello World", "World")

The result is:

Yes
No

bind

Join collection values into text:

items = ["A", "B", "C"]

result = bind(items, ", ")

Result:

A, B, C

sever

Split text into a collection:

sever("A,B,C", ",")

Result:

["A", "B", "C"]

cut

Remove surrounding whitespace:

cut(text)

raise

Convert text to uppercase:

raise(text)

lower

Convert text to lowercase:

lower(text)

Boards

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

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.


Returning values

Use give:

job square(x) {
    give x * x
}

A function that finishes without giving a value produces:

EMP

Groups (objects and methods)

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.

Declaring a group

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 job inside the block declares a method. Methods use the ordinary job syntax (job name(params) { ... }) and are not callable as flat top-level jobs.
  • Inside a method, self is the current instance. self.field reads a field; self.method(...) calls a method — including an inherited one.

Constructing instances

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.

Field access and method calls on instances

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.

Inheritance

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: Employee has name, age (from Person) and role (its own), in that order.
  • Inherited methods can be called directly: self.introduce() inside describe() calls Person's method.
  • A child method with the same name as a parent's overrides it for the child.
  • super is not implemented: super.method() is a compile error (undefined identifier 'super').

Canonical example

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).

Restrictions and unsupported behavior

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 = 3 is not supported; fields can only be read with self.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 ... in over 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>.

Input and output

Read input with take:

name = take("What is your name: ")

Display values with show:

show("Hello")
show(name)
show(10 + 5)

Conditions

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}!")
}

Loops

Fly provides while and for.

while

while condition {
    ...
}

for

for item in items {
    show(item)
}

Iteration semantics depend on the iterable value.


Loop control

Fly uses:

skip
getout

skip skips the remainder of the current iteration.

getout exits the current loop.


wait

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.


Error handling

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.


Type casting

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.


Modules

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.


Built-in modules

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 modules

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.


HTTP and networking

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.


Memory management

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.


FlyValue

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.


Runtime architecture

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.


Compiler architecture

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 executables

The Fly toolchain is divided into clear roles.

fly.exe

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.


fly-cc.exe

The native Fly compiler.

It handles:

lexer
parser
semantic analysis
IR generation
LLVM
linking
native executable generation

fly-repl.exe

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.


The REPL

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.


Toolchain updates

Fly is designed to update itself through the same toolchain.

The intended command is:

fly -up

Currently 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.


Windows installation

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.


Native Windows executables

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.


Portability

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.


Project structure

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 []

Complete development workflow

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.


Language philosophy

Fly does not attempt to reproduce another programming language.

Its design priorities are:

  1. Readability
  2. Simplicity
  3. Consistency
  4. Expressiveness
  5. Practical native compilation
  6. A distinctive Fly vocabulary

The goal is not to make every feature configurable.

The goal is to make the common path pleasant.


Python-inspired, not Python-compatible

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.


Fly 0.1.6 language specification

This section summarizes the current core Fly language.

File extension

.fly

Comments

$ comment
$$
comment
comment
$$

Core values

tex
num
dec
yn
coll
board
emp

Boolean literals

Yes
No

Empty value

EMP

Variables

a = 10

Immutable:

hard a = 10

Operators

Arithmetic:

+
-
*
/
%

Comparison:

==
!=
<
>
<=
>=

Logical:

and
or
not

Strings

message = "Hello"

Interpolation:

show("Hello {name}")

Escaped braces:

show("Use {{name}} literally.")

Collections

items = [1, 2, 3]

Index:

items[0]

Slice:

items[1:3]
items[:3]
items[2:]
items[:]

Boards

person = {
    "name": "Rick"
    "age": 18
}

Functions

job add(a, b) {
    give a + b
}

Groups

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.

Input/output

name = take("Name: ")
show(name)

Conditions

if condition {
    ...
}
orif other_condition {
    ...
}
ifnot {
    ...
}

Loops

while condition {
    ...
}
for item in items {
    show(item)
}

Loop control

skip
getout

wait

wait 0.5

wait sleeps for a num or dec number of seconds (non-numeric values are runtime errors; negatives clamp to 0).

Error handling

do {
    ...
}
grabe (err) {
    ...
}

Casting

num("42")
dec("3.14")
tex(123)

Modules

bring path
bring hashbox as hb

Reserved keywords

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 status

Fly 0.1.6 — Language and toolchain milestone

Current development version: 0.1.6

Current implemented areas include:

  • Dynamic typing
  • Mutable variables
  • hard immutable variables
  • tex
  • num
  • dec
  • yn
  • coll
  • board
  • emp
  • Arithmetic
  • Comparison
  • Logical operators
  • Indexing
  • Slicing
  • String interpolation
  • Functions
  • Conditions
  • while
  • for
  • skip
  • getout
  • Groups (objects and methods): group, instances, fields, methods, self, from single inheritance
  • wait statement
  • 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 hashbox public 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.


Example: a complete small program

job greet(name) {
    if name == EMP {
        give "Nobody"
    }

    give name
}

name = take("What is your name? ")

message = greet(name)

show("Hello, {message}!")

Example: collections

items = ["Fly", "SLEEP", "Dump"]

attach(items, "LLVM")

for item in items {
    show(item)
}

show("Items: {count(items)}")

Example: error handling

input = take("Enter a number: ")

do {
    number = num(input)
    show("You entered {number}.")
}
grabe (err) {
    show("That wasn't a valid number.")
}

Example: module usage

bring http

response = http.get_tls("https://example.com")

show(response["status"])

Example: project

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 -build

Run:

fly -run

Design principle: batteries included

Fly'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.


Binary and systems roadmap

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.


Roadmap

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.


Contributing

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.


Licensing

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.


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.

About

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.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages