madc v0.82.0: Linux, macOS and Windows now supported
▲ 6 r/AIprogrammingLanguage+2 crossposts

madc v0.82.0: Linux, macOS and Windows now supported

I just released madc v0.82.0, and this is probably the biggest portability milestone for the project so far.

madc already supported Linux, and v0.76.0 added the first public macOS builds. With v0.82.0, Windows joins them, and all three platforms now ship together from the same source tree:

  • Linux
  • macOS — Apple Silicon and Intel
  • Windows 11 / Win64

Public binaries are now available for all three.

A few highlights from this release:

  • Three-platform releases from one tree — Linux, macOS and Windows are now built and validated together.
  • Headerless operation on Linux, MacOS and Windows — madc can compile programs using its own embedded standard-library corpus even when there are no system headers or development tools installed.
  • JIT and native AOT work on all three platforms
  • Lots of various bugfixes along the way
u/kindredseer — 3 days ago
▲ 5 r/AIprogrammingLanguage+1 crossposts

madc v0.76.0: macOS support joins Linux — Apple Silicon + Intel

I just released madc v0.76.0, which adds the project's first official macOS support alongside the existing Linux support.

madc has already been running on Linux; this release brings the same general experience to Macs, with prebuilt releases for both arm64 (Apple Silicon) and x86_64.

The macOS tarballs are designed to work even on a header-less Mac. madc carries its packed C/C++ standard-library environment with it, so things like <string>, containers, streams, and <algorithm> can compile directly from the embedded image.

A few highlights:

  • Existing Linux support, now joined by macOS
  • Prebuilt macOS binaries for Apple Silicon and Intel
  • JIT compilation works on macOS
  • Native AOT executable generation works for both C and C++
  • madc -o prog prog.mad can produce a runnable Mach-O executable
  • C++ standard-library headers are available from madc's embedded frozen forest
  • --emit=c11 output can be compiled with the system compiler using the included libmadc_rt
  • A major AArch64 ABI fix now correctly handles C++ objects returned by value
  • Several additional libc++ and macOS compatibility fixes landed along the way
  • The integration test suite has grown to 1,019 tests, with the primary JIT, EXE, OBJ, packed, and release lanes all passing

One of the more interesting parts of getting macOS working was discovering how many assumptions that are fine on x86-64 stop being true on Apple Silicon. In particular, AArch64 handles the hidden return pointer for larger C++ objects differently, so madc now lets the target ABI decide where that parameter belongs instead of assuming the x86 convention.

For me, the bigger milestone is that madc is becoming something you can simply download and try on either Linux or macOS as a lightweight C/C++ scripting environment, rather than first treating it as a compiler project you need to build and configure yourself.

Recent releases have also added things like var dynamic variables, URI-based channels for files/TCP/processes, streaming data support, and substantially faster loading of C++ headers.

The direction I'm aiming for is essentially:

keep C/C++ available underneath, but make writing small programs feel much closer to scripting.

v0.76.0 is the release that brings Mac users into that experience too (Windows coming soon).

u/kindredseer — 8 days ago

MadC v0.75 Release

Version 0.75 of MadC was released yesterday, I'm still working on proper MacOS support, but its getting closer... should be ready this week. For now, I've got some more bugs fixed, more C++ speed improvements, and some madc specific language features and improvements:

  • var dynamic variables — madc now has a built-in var type that can hold strings, numbers, booleans, arrays, and other values without needing to declare a fixed type up front
  • Simple file and network channelsmadc::channel provides one straightforward interface for reading and writing files, TCP connections, and other data sources
  • Run programs through exec:// — scripts can launch another program, send data to it, and read its output almost like working with a file

Example:

channel sorter("exec://sort");
defer { sorter.close(); }

if ( !sorter.ok() )
{
    printf("open failed: %s\n", sorter.last_error());
    return 1;
}

sorter.write("pear\napple\nmango\n");
sorter.close_write();

var line;
while ( sorter.readline(line) )
    printf("sorted: %s\n", line);
sorter.close();
u/kindredseer — 9 days ago
▲ 10 r/AIprogrammingLanguage+10 crossposts

Ai writen language, if anyone wants to try it out~

Been a few weeks working on this, majority of it was done in like 10 days, but then life, and bugs needed fixed and feature creep, and everything. Either try it out or not, I needed something specific that I had more control over for my other projects, so now this exists.

github.com
u/gusfromspace — 10 hours ago
▲ 7 r/AIprogrammingLanguage+1 crossposts

MeScript (A musical programming language inspired by Strudel and SuperCollider)

I have been working on this language since May. Its heavily inspired by Strudel and partially by SuperCollider. It works as many things all at once. It is a music sequencer, synthesizer, fx box, generative music (conditional statements, if, else) and sample mangling (need non-browser version for that), automation, per note automation, recursion, easy nesting of gradually more complex sequences/fx/etc.

If anyone wants to try the language, its here, https://quadracollision.com/mescript/ mostly usable in browser unless you want to use samples, if so just let me know.

As for tools: This is GPT 5.3 -> GPT 5.4 -> GPT5.5 -> GPT 5.6 Sol, all Medium. This is Rust+WASM with a Javascript front end. No external libraries

This is an example of MeScript in action: you can paste this into the editor and it'll work.

Check CTRL+H for insertable smart forms, cursor position defines how the form is inserted. Also check out the language reference guide. There is also a repl, place the cursor on any line and right click and press parameter help and it will explain what the line does. Also (fix-parens linenumbers) (fix-parens 15-17) can help with parenthesis issues, this is a Lisp inspired language.

(def am [as d f]) ;this is a chord form, asharp d and f

(ins :saw  ;this is an (ins)rument form
   :src [:saw-synth :square-synth :additive] ;these are the oscillators that make up this instrument
   :note (p (times 4[c4 0 c5 0 e3 c3 e3])    ;these are the note gates and how many
            (times 4[c4 0 c5 0 e3 c3 eb3]))  ;times they play
   :gate nil
   :fx [(formant :vowel a :mix 0.2)]    ;fx applied to entire instrument
   :dur nil    ;duration of each gate hit, can be per hit as well 
   :amp nil    ;amplification of instrument, can be per hit and automated conditonally
   :voice nil) ;special keyword, see ctrl+h for voice commands, as well as everything else

(def chord_1 [c4 eb4 f4 gb3])

(ins :kick
   :src :kick-synth
   :note (p [c4 0 0 0 c4<?50> 0 0])
   :gate nil
   :fx nil
   :dur nil
   :amp nil
   :voice nil)

(ins :hat-808
   :src :hat-808
   :note (p [c7 c7 <c4%c5%c7> 0 c7 <c7%e5%b4%g5> c7 am(3 3 3)]) ;this calls the chord form with each note being an octave of 3
   :gate nil
   :fx nil
   :dur nil
   :amp nil
   :voice nil)

(ins :snare-808
   :src :snare-808
   :note (p [c4<?30> 0 0 c4<?50> 0 0 0 0])
   :gate nil
   :fx nil
   :dur nil
   :amp nil
   :voice nil)

(ins :pad-wash
   :src :pad-wash
   :note (p [chord_1<_7> 0 0 0 0 0 0 0])
   :gate nil
   :fx nil
   :dur nil
   :amp 0.05
   :off true
   :voice nil)
reddit.com
u/kindredseer — 13 days ago
▲ 2 r/AIprogrammingLanguage+1 crossposts

Desi v0.1.0 — Python-ish syntax, no GC, and three optimisations that made it slower

I've been building Desi for about a year — it's the third or fourth rewrite — and I tagged v0.1.0 today. Compiled through LLVM, no garbage collector, indentation-based syntax, and a standard library that already has an ORM and an HTTP server in it.

Two things up front: I leaned on AI heavily to build this, and I'd call v0.1.0 a working prototype rather than a finished language. More on both at the end, including what I'm hoping to get out of posting here.

Rather than list features, here are three things I was confident about that turned out to be wrong. All three cost me under an hour to test and would have cost days to discover afterwards.

1. Turning on LTO made it slower.

Element access (xs[i], xs.append(v)) was an out-of-line call into the runtime, so the optimiser couldn't see through it. The obvious fix was to let it inline. I measured 1M appends + 1M reads:

out-of-line call        3.28 ms
inlined                 4.22 ms   <- worse

The bodies end in fprintf(stderr, ...) on their failure paths. Inlining drags a varargs call and its setup into the loop. Adding one file to the LTO set — a one-line change — would have been a regression.

What works is the shape Rust's Vec uses: the bounds check inline, the panic #[cold] and out of line. Same code, error paths pushed out:

same semantics, cold errors    1.09 ms   <- 3x

I ended up emitting the check and load directly in the IR with a cold call to the runtime on failure, rather than depending on LTO, since LTO isn't available on every platform I build for.

2. "The representation is the bottleneck" was wrong.

A list element is a pointer-sized slot, ints stuffed in directly. I assumed that indirection was costing me and planned a typed-storage rewrite. Measured:

typed int64[] array, inlined    0.76 ms
void* slots, inlined            0.74 ms

Identical. An int64 and a void* are both 8 bytes in a contiguous array — there's no indirection to pay for. I'd have spent a week rewriting collection storage for nothing.

3. Except for floats, where it was 24x.

Same slot representation, but a double wasn't stuffed in — each one got its own malloc. A comment in my codegen said "can't bitcast float to ptr," which is true, and why past-me reached for the allocator. But you can bitcast a double to an i64, and i64s were already going into slots.

boxed         28.65 ms
by value       1.18 ms

That change deleted more code than it added, and two other benchmarks reached parity with C without being touched — the boxing was most of what they'd been measuring.

Where it actually is

Against equivalent C at -O2, whole-process wall time on Linux:

Function Desi C
string_churn 12 ms 15 ms
binary_tree 2 2
quicksort 2 2
loop_sum 1 1
list_ops 6 4–6
matrix_mul 2 1
dict_ops 9 5
fib_recursive 10 6
alloc_churn 6 1

Five of eleven match or beat it. Two clearly don't: alloc_churn allocates a small collection half a million times, and dict_ops still calls into the runtime per operation. Both are understood, neither is mysterious, and I'd rather name them than average them away.

Memory

No GC, no manual free — the compiler inserts cleanup where a value's owner goes out of scope. What's guaranteed is no use-after-free and no double-free in safe code. What's not guaranteed is freedom from leaks: where ownership can't be proven, the compiler leaks rather than frees. Heap elements inside a collection — the strings in a list[str] — are the main case, and it's documented rather than hidden.

Unclear ownership never resolves to "free and hope". That's the property that let me ship these optimization incrementally: every analysis is allowed to be dumb, because being wrong costs memory, never corruption.

What it isn't

Supervisors exist and restart failed tasks, but this is not OTP — a child is a task, not another supervisor, so nothing escalates up a tree. Elixir's model doesn't port cleanly to shared-memory threads, and I'd rather ship the honest subset than borrow the name.

There's also no compile-time data-race checking. Channels and locks are there; nothing verifies you used them correctly. That's the biggest gap versus Rust and I don't have a good answer yet — though I suspect it's the same question as "what may safely cross a channel," which would also be the route to real supervision.

How it was built, and what I'm after

I should be upfront: I used AI heavily throughout this, and I'm not a compiler engineer by background. The three stories above are the reason I trust any of it — every one is a case where a confident-sounding plan was wrong and a measurement caught it before it shipped. "Enabling LTO will make this faster" is exactly the kind of thing that sounds right and isn't. So the rule became: measure first, and let the number decide. Anything I couldn't measure, I wrote down as unproven rather than claiming it.

Which is also why I'd call v0.1.0 a working prototype rather than a finished thing. It compiles, the test suite passes on three platforms, and the docs say what's broken. But there are decisions in here that someone who has actually built a type system or a borrow checker would look at and immediately improve — the data-race question especially, and probably the escape analysis.

That's what I'm hoping to find here. If you know this territory and something in the above made you wince, I'd genuinely rather hear it than not. Contributions welcome, but honestly even a "you've modelled this wrong and here's why" comment is worth more to me right now than a star.

Links

The name Desi (દેશી) means "local" or "native" in Gujarati, which is my first language.

Happy to go into any of the above — the measurement stuff especially, since I have numbers for most of what people usually ask.

reddit.com
u/kindredseer — 15 days ago
▲ 4 r/AIprogrammingLanguage+1 crossposts

AI-friendly programming language design

So when developing a new programming language using AI (or enhancing an existing one), it seems to make sense to design the language not only to be convenient for humans to use, but likewise for AI agents.

What Would an “AI-Friendly” Programming Language Actually Look Like?

There has been a great deal of discussion about making programming languages easier for AI systems to use. Usually, this means making code easier for large language models to generate: simpler syntax, fewer punctuation rules, less boilerplate and more predictable formatting.

But code generation is only a small part of software development.

An autonomous programming agent must also be able to understand an unfamiliar codebase, identify the consequences of a change, modify the program without breaking unrelated behaviour, verify that the result is correct and explain what it has done.

That suggests a much broader definition:

>

The goal should not merely be to make programs easier for AI to write. It should be to make programs easier for both humans and machines to understand, modify and verify.

Source Code Should Not Be the Only Representation

Most programming languages treat source text as the authoritative representation of a program. Compilers parse that text into abstract syntax trees, symbol tables, control-flow graphs and other structures, but these are usually treated as temporary implementation details.

An AI-friendly language could instead expose a stable semantic representation of the program.

Humans might continue to work primarily with readable source code, while tools and AI agents interact with the same program as a structured semantic graph containing:

  • Symbols and their identities
  • Types and relationships
  • Data ownership
  • Function contracts
  • Side effects
  • Dependencies
  • Call graphs
  • Tests
  • Access permissions
  • Source locations
  • Documentation

Source code would remain important, but it would become one view of the program rather than the only usable representation.

An AI agent should not need to rediscover the meaning of a program by repeatedly parsing text, searching filenames and inferring relationships from naming conventions.

Programs Should Be Locally Understandable

One of the greatest difficulties in maintaining a large codebase is that the meaning of a small section of code may depend on information scattered throughout the repository.

A module might rely on global state, build flags, initialization order, implicit imports, runtime configuration or code-generation steps that are not visible locally.

This is difficult for humans and even more difficult for AI agents operating with limited context windows.

An AI-friendly language should encourage modules to explicitly declare:

  • What they export
  • What they import
  • What state they own
  • What resources they require
  • What external systems they access
  • What assumptions they make
  • What invariants they guarantee

For example:

module Accounts

exports:
    User
    update_email

requires:
    Database
    EmailService

owns:
    UsersTable

guarantees:
    User.email is normalized
    User.email is unique

The purpose is not necessarily to make source files more verbose. Much of this information could be inferred by the compiler and displayed through tooling.

The important part is that the information exists in a structured and queryable form.

A programming agent should be able to ask:

describe module Accounts

and receive a bounded, reliable summary of the module without examining the entire application.

Types Should Describe Meaning, Not Just Storage

Many programming languages describe data primarily in terms of its storage representation.

A program may represent all of the following as strings:

first_name
email_address
postal_code
country_code
birth_date
telephone_number

Although these values share a storage representation, they do not share a meaning.

A language designed for machine reasoning should support semantic types such as:

PersonName
EmailAddress
PostalCode
CountryCode
BirthDate
TelephoneNumber
CurrencyAmount
TimeZone

These types could carry information about:

  • Valid values
  • Normalization
  • Comparison
  • Serialization
  • Privacy
  • Localization
  • Appropriate user-interface controls
  • Database representation
  • Safe conversions

A value of type EmailAddress would not merely be a string whose purpose is explained in a comment. Its meaning would be available directly to the compiler, development tools and AI agents.

This reduces the need to infer domain knowledge from variable names and scattered validation code.

Effects Should Be Part of Function Signatures

Traditional type systems tell us what values a function accepts and returns, but often say very little about what the function can do.

Consider a function such as:

update_email(user, address)

Does it modify memory? Write to a database? Send an email? Update an audit log? Access the network? Throw an exception? Trigger an event?

An AI agent should not need to inspect the implementation and every transitive function call to answer these questions.

An effect-aware declaration might look something like this:

function update_email(user_id, new_address)
    returns Result

    reads:
        User.id
        User.email

    writes:
        User.email
        AuditLog

    uses:
        Database
        EmailService

    may:
        send_email
        fail_with ValidationError
        fail_with DuplicateEmailError

This creates a machine-readable description of the function’s blast radius.

An agent proposing a modification could ask:

show effects of update_email

or:

will this change introduce network access?

The compiler could provide a reliable answer.

Authority Should Be Explicit

Most programs execute with ambient authority. Any code running within the process may be able to access the filesystem, environment variables, network, database or global application state.

That is convenient, but dangerous when code is being produced or executed by an autonomous agent.

A more AI-friendly language would use capabilities: explicit values representing permission to access particular resources.

For example:

function load_config(config_directory)

The function could access only the directory represented by the capability it receives. It would not automatically inherit access to the entire filesystem.

Similarly:

function update_customer(read_write_customers_database, customer_id)

could write to the customer database but not to the payroll database.

This would allow an AI agent to operate within a restricted environment where accidental or malicious actions are structurally impossible.

The agent could be given:

  • Read access to one repository
  • Write access to one module
  • A temporary filesystem
  • A test database
  • No network access
  • A limited memory and execution budget

Security boundaries would become part of the program rather than an external policy layered on top of it.

Contracts Should Be First-Class

Comments can explain what a function is intended to do, but comments are not normally checked by the compiler.

An AI-friendly language should make preconditions, postconditions and invariants first-class program elements.

For example:

function transfer(source, destination, amount)

requires:
    amount > 0
    source.balance >= amount

ensures:
    source.balance =
        previous(source.balance) - amount

    destination.balance =
        previous(destination.balance) + amount

    source.balance + destination.balance =
        previous(source.balance + destination.balance)

These contracts could serve several purposes:

  • Human documentation
  • Static analysis
  • Runtime checks during development
  • Test generation
  • Formal verification
  • Agent acceptance criteria

Instead of guessing whether an implementation is correct, an AI agent could ask the compiler whether the implementation satisfies its declared contract.

Contracts would also make tasks easier to define.

Rather than telling an agent:

>

a task could be expressed as:

Modify transfer so that contract
AccountTransferPreservesTotalBalance
is satisfied.

The desired outcome becomes concrete and machine-verifiable.

Inference Should Be Predictable and Inspectable

Inference can make a language substantially easier to use. Type inference, automatic imports, generic specialization and implicit conversions can remove large amounts of repetitive code.

However, inference becomes dangerous when it hides meaningful decisions.

A useful rule might be:

>

Automatic behaviour may be reasonable when:

  • A conversion is exact
  • Ownership remains unchanged
  • No persistent state is modified
  • No external resource is accessed
  • Execution remains deterministic

Explicit syntax should be required when an operation:

  • Loses information
  • Transfers ownership
  • Performs network access
  • Blocks or becomes asynchronous
  • Writes persistent state
  • Escalates privilege
  • Introduces nondeterminism
  • Has significant computational cost

An AI agent should also be able to inspect every inferred decision.

For example:

explain expression:
    total = price + tax

might produce:

price has type Currency<USD>
tax has type Currency<USD>

selected operation:
    Currency.add

conversion:
    none

effects:
    none

possible failures:
    CurrencyOverflow

The language could remain concise while the toolchain exposes the full semantic interpretation.

The Language Should Have a Canonical Form

Formatting tools create a consistent textual style, but an AI-friendly language would benefit from a deeper canonical representation.

The compiler could normalize:

  • Resolved names
  • Inferred types
  • Selected overloads
  • Implicit conversions
  • Generic arguments
  • Default arguments
  • Effects
  • Ownership decisions

Human-written code might say:

user.balance += payment

The canonical semantic form might record:

read field User.balance

convert Payment
    to CurrencyAmount
    using exact conversion

invoke CurrencyAmount.add

write result
    to field User.balance

This form would not necessarily be shown during normal programming. It would be available to tools, reviewers and agents when precise interpretation is required.

It would also allow code written in different stylistic forms to be compared semantically rather than textually.

Symbols Should Have Stable Identities

Programming tools often identify symbols using names and source locations. Both are fragile.

Names change during refactoring, and source locations change whenever lines are inserted or removed.

An AI-friendly language could assign stable identities to program entities:

symbol:
    User.email

stable_id:
    field:7f2a81c4

aliases:
    email
    email_address
    contact_email

Humans could use whichever names are appropriate in source code or user interfaces, while tools and agents refer to the canonical identity.

This becomes especially important when a language supports aliases, localization, schema evolution or generated interfaces.

An agent should be able to rename a symbol without losing track of its identity or confusing it with another similarly named symbol.

Changes Should Be Semantic, Not Merely Textual

AI coding tools currently make changes largely through text patches. This works, but it is fragile.

A line-based patch may fail because:

  • The file was reformatted
  • Another change shifted the lines
  • A symbol was renamed
  • Similar code appears elsewhere
  • The surrounding context has changed
  • The patch applies cleanly but to the wrong location

A semantic patch could instead express intent:

rename symbol:
    User.birthdate
to:
    User.birth_date

or:

add parameter:
    Logger

to function:
    process_order

position:
    after Database

or:

replace implementation of:
    Account.transfer

only if:
    function signature is unchanged
    semantic hash matches 4e720
    no new callers have been introduced

The compiler or development environment could translate the semantic change into ordinary source-code edits and Git-compatible diffs.

The repository would still contain readable text, but agents would operate on program structure rather than guessing where to insert characters.

Diagnostics Should Be Structured Data

Compiler errors are generally written as prose for humans:

Cannot convert argument 2 from nullable string
to email address.

An agent then has to parse that prose and infer an appropriate repair.

A structured diagnostic might contain:

diagnostic_code:
    TYPE_ARGUMENT_MISMATCH

function:
    update_email

parameter:
    new_address

expected:
    EmailAddress

received:
    Nullable<String>

cause:
    nullability mismatch

possible_repairs:
    validate non-null value
    provide default value
    change parameter type

automatic_repairs:
    none

The human-readable message could still be generated from this data.

The compiler should clearly distinguish between:

  • A repair that is known to preserve semantics
  • A probable repair requiring review
  • Multiple ambiguous alternatives
  • A condition for which no valid repair is known

This would make compiler interaction far more reliable for autonomous agents.

Relationships Should Be Declarative

Many applications define the same relationship repeatedly across:

  • Database schemas
  • Object models
  • API definitions
  • Validation code
  • User interfaces
  • Serialization formats
  • Access-control rules

An AI agent must then determine whether these duplicated definitions are consistent.

A more declarative language might express the relationship once:

entity Order

fields:
    id: OrderId
    customer: relation to Customer
    items: many OrderItem
    total: CurrencyAmount

derivation:
    total = sum(items.price)

The language now knows:

  • customer refers to another entity
  • items is a collection
  • total is derived
  • Changing an item may change the total
  • total should not normally be edited directly
  • Storage and user-interface tools can represent these fields appropriately

This greatly reduces the amount of detective work required to understand the application.

State Changes Should Be Transactional and Inspectable

AI-generated operations should be easy to preview, sandbox and reverse.

A language or runtime could make state-changing operations transactional:

transaction UpdateEmail

set:
    user.email = new_email

append:
    audit_log = email_changed

send:
    confirmation_email

Before committing, an agent or human could request:

preview transaction UpdateEmail

The runtime might report:

database changes:
    Users.email modified for user 1842
    AuditLog row inserted

external effects:
    one email would be sent

invariants checked:
    email is valid
    email is unique

result:
    transaction may commit

The runtime could also support:

  • Snapshots
  • Rollback
  • Deterministic replay
  • Resource limits
  • Mutation logs
  • Simulated external services
  • Reversible development environments

An agent should be able to demonstrate what would change before being permitted to change it.

Tests Should Be Connected to Program Semantics

Tests are usually organized as source files and function names. Their relationship to the code they verify is often informal.

An AI-friendly language could associate tests with symbols, contracts and invariants:

test transfer_preserves_total

verifies:
    Account.transfer

covers:
    AccountTransferPreservesTotalBalance

The toolchain could then answer:

which tests verify Account.transfer?


which public behaviours changed?


which contracts have no tests?


what is the smallest sufficient test set
for this patch?

This last question is particularly important for autonomous agents. Running every test after every small change may be expensive, while running too few tests is unsafe.

Semantic test relationships could allow the compiler to select a targeted verification set and then expand it when uncertainty remains.

The Compiler Should Expose an Agent Protocol

The most important feature may not be part of the language syntax at all.

A language designed for AI-assisted development should provide an official interface through which agents can query and modify programs.

That interface might support operations such as:

describe(symbol)

find_references(symbol)

explain(expression)

show_effects(function)

show_contracts(function)

show_invariants(type)

calculate_change_impact(patch)

create_semantic_patch(request)

validate_patch(patch)

find_relevant_tests(patch)

run_tests(test_set)

preview_transaction(operation)

apply_patch(patch)

rollback(change)

Today, AI coding agents often interact with a repository using little more than shell commands, text search, a language server and compiler output.

A compiler-native protocol would give them a much more precise and constrained environment.

The compiler could even generate a compact task-specific context package:

task:
    modify email validation

relevant symbols:
    User.email
    EmailAddress
    update_email
    UserRepository.save

required invariants:
    email must be normalized
    email must be unique

allowed modules:
    Identity
    Accounts

forbidden effects:
    database schema changes
    network access

required verification:
    EmailAddress contracts
    identity email tests

This would help prevent agents from becoming lost in large repositories or long-running plans.

AI-Friendly Does Not Necessarily Mean Verbose

Many of these ideas may sound as though they would produce an extremely ceremonial language.

That does not have to be the case.

Humans should be able to write concise code while the compiler derives and records the richer semantic model.

For example:

function add_item(order, item):
    order.items.append(item)

The compiler might infer:

  • order is mutated
  • item is read
  • order.total must be recalculated
  • The operation may fail if the order is finalized
  • The database transaction touches two tables
  • Three invariants must be rechecked
  • Four tests are directly relevant

The source code remains readable. The additional information exists because the language and toolchain understand the operation.

The principle should be:

>

Human-Friendly and AI-Friendly Design Are Closely Related

Most of the features that would make a language safer for AI agents would also make it easier for humans to maintain:

  • Explicit module boundaries
  • Meaningful types
  • Predictable conversions
  • Structured effects
  • First-class contracts
  • Better diagnostics
  • Semantic refactoring
  • Transaction previews
  • Clear test coverage
  • Stable symbol identities

AI agents amplify the importance of these features because they expose weaknesses that humans have historically worked around through experience, intuition and institutional knowledge.

A human developer may remember that a particular field is updated by a hidden database trigger. An AI agent may not discover that fact until something breaks.

The better solution is not necessarily to train the agent to guess more accurately. It is to make the dependency explicit.

A Possible Definition

A genuinely AI-friendly programming language would not merely be one whose syntax appears frequently in training data.

It would be a language in which a program is:

  • Readable as source code
  • Understandable as a semantic graph
  • Divided into bounded cognitive domains
  • Explicit about authority and side effects
  • Modifiable through structured operations
  • Testable against declared contracts
  • Executable in sandboxed transactions
  • Verifiable using compiler-supported evidence

The central design goal might be summarized as:

>

Such a language would not eliminate programming mistakes, hallucinations or unsafe changes. It would, however, give both humans and AI agents much stronger tools for detecting those problems before they reach production.

The result would not simply be a language that AI can generate.

It would be a language in which AI can operate with bounded authority, explicit understanding and evidence that its work is correct.

reddit.com
u/kindredseer — 17 days ago
▲ 1 r/AIprogrammingLanguage+2 crossposts

Introducing Mad-C (My Advanced Dialect of C++)

So just over seven years ago I got the idea that I wanted to try my hand as writing my own actual programming language, and by this I mean more than just a scripting language, as I had made a few of these over the years... the first one being a simple scripting language for a dial-up BBS terminal program I co-authored with a friend to script playing MajorBBS games, primarily one called Galactic Empire written by Mike Murdoc which my friend was majorly into, and hence called the terminal program GEnius. It was written in Turbo Pascal, and somewhat modeled after the DOS terminal program Telix. I specifically worked on a scrollback buffer that would display everything in full ANSI and also the (very rudimentary) script language.

Fast forward nearly 30 years and I'm experimenting with writing my own byte code interpreter and direct dispatch switch tables getting decent performance, and then I stumbled across a library called AsmJIT which made it relatively easy to generate x86 code and execute it, and I was off to the races working on my own c-like language which I originally called C3PO.

After working on it for a few weeks, I changed the name to Mad-C before creating a github repo (pretty much exactly 7 years ago today), and pushed forward getting all the basics working, if/else, expression parsing, switch, for loops, etc, before trying to figure out what was going to make it stick out beyond it being a JIT language, and I decided I wanted to bring in a bit of C++, and that it was also going to bring in features from other languages.

I tinkered around with it on and off for several months, but actually getting C++ features working properly (without crashing) turned out to be much more difficult than getting C working, and eventually I just didn't have the time to continue to chip away at it as I got busy with other aspects of work and life... it was something I would tinker with here and there as I could.

After a certain point, the AsmJIT author had changed the interface and deprecated some ways of doing things, and I ended up shelving the project completely. It was fun while it lasted, but I just didn't have the time to pursue it further.

Fast forward to this year, and my work started pushing AI really hard, such that all the lead developers (including myself) got pulled off of our primary projects for three months to completely focus on implementing an "AI Playbook" using Claude Code.

I was so impressed at how much better this was working for me than my previous experience with Cursor, that I ended up purchasing my own personal subscription to try it out on my personal projects, including Mad-C, and it was able to get it building again.

I switched back and forth between using Claude Code as well as Codex (which I had barely tried before this) and got Mad-C to the point where I was able to get it to JIT run the SMAUG MUD code base (which is over 185 Kb of C89-style C source).

This wasn't instant... it took weeks of grinding away switching between Codex and Claude Code, waiting for 5 hour usage resets, but it was actually working and I was steering it and guiding them all along the way. Part of getting this working involved grinding it against the C23 GCC torture test suite.

I also wanted it to be able to cache the code generation in object files, as well as generate executables. This involved a lot more complexity and all test cases needed to pass in both JIT mode and EXE mode, and required implementing my own IR (intermediate representation).

Next I started adding in those language features from other languages. I used C++ style namespaces to bring in about 100 different functions from languages like PHP, Perl, Python, Rust, Ruby, JS, etc, as well as things like defer, multiple return values, rust matching, etc, and then turned the focus onto C++ support.

This is where things started getting really tricky because I didn't just want to simulate C++ with hardcoded string, fstream, and stringstream classes, I wanted it to parse the real C++ headers, which meant templates, and multiple inheritance, among hundreds of other language features.

Also, around the same time, just to make things more exciting, I wanted to be able to support other architectures than only x86 Linux, and this is when I discovered the MIR project, which was also seven years old. It provided a CPU agnostic "medium intermediate representation" (MIR), and seemed like just what I was looking for... but it presented a fork in the road, because not only did it provide this CPU agnostic opcode format, the project also included a C11-to-MIR library.

So the question now was, did I switch all my code from generating x86 code of my own design and structure to generating MIR opcodes, or do I change Mad-C into a sort of C-transpiler?

The author of MIR (Vladimir Makarov) has been one of the GCC developers for over 20 years... so I figured his C-implementation was likely superior, and it was also tested on MIR itself (which is written in C), and I chose this direction instead, even though I already had a working C implementation.

I did keep my lexer/parser though. What I ended up doing, was taking the internal node structure (the node_t struct) and using it for the base of MadC's AST tree node, which I named CIR_node, and it contains all the semantics for C++ and MadC.

I modified libc2mir so that I could pass in the CIR_node AST tree directly, which c2mir thinks is its own node_t AST tree, and converts it to MIR, and JIT executes it.

So MadC parses C, C++ (and madc), but lowers C++ into a C-AST tree, similar to how CFront used to work (the original C++ implementation). This means that madc can also emit standard C code so that you can feed it to GCC or CLANG if you want to.

The other part of MadC that was important to me, was making it self-contained, so the build also packs in all the system headers, precompiled into an AST "forest", and appends this to the binary compressed. This currently weighs in at around 12 Mbs or so total. So with a 12 Mb binary you can have a C/C++ JIT language (plus compiler) that doesn't need any external system include files.

Not only that, but it also support auto-including, so you don't need to remember what function or object is in what header file. It also supports auto-namespace resolution as well as an auto-main "script mode" where you do not even have to define a main() function.

I didn't stop there, of course, and MadC depends on my own fork of MIR, where I've been working to add C23 support (MIR's c2mir only implements the C11 standard), and I've also added support to my fork of MIR to generate objects, ELF binaries, link objects, handle multi-file projects, and I'm currently working on this for different platforms, like Mach-O for MacOS, as well as ARM CPUs.

This ended up being much more complicated than expected because while Linux uses libstdc++ (which is the GCC implementation of C++), MacOS uses libc++ (the CLANG implementation of C++) and they are quite different. So this is still in progress, but getting close to completion.

So now that you have all the history, please take a look at the project, and let me know what you think! -- https://github.com/derekbsnider/madc

u/kindredseer — 18 days ago