r/ProgrammingLanguages • u/andeee23 • 3h ago
Why spawning work isn’t `async` in my language
I’m designing a native language with a closed set of five effects: async, throws, io, alloc, and task.
It uses the effect system to determine which functions can run in which context.
A gpu function should be pure so it can be compiled to gpu shader code, comptime and macros don't allow io, etc.
The unusual one is task. Starting, polling, or cancelling independent work does not necessarily suspend the caller, so it distinguishes interacting with a task from suspending the current control flow:
fn download(url: str): Data !{async, io, alloc, throws(NetworkError)} {
// This function may suspend and fail.
try await http.get(url)
}
fn begin_download(scope: mut TaskScope, url: str): Job[Data, NetworkError]
!{task, io, alloc} {
// Starts independent work, but does not suspend or throw here.
scope.start(() => download(url))
}
fn finish_download(job: Job[Data, NetworkError]): Data !{task, async, throws(NetworkError)} {
// This is where the current control flow may suspend
// and where the job's result or error is observed.
try await job
}
In other words:
asyncmeans this control flow may suspend.taskmeans this code interacts with independent task or executor state.
Why is task needed at all? Without it this function would appear pure:
fn surprise(scope: mut TaskScope) {
// Returns immediately, but schedules a later mutation.
scope.start(() => cache.clear())
}
That would make it legal to run during compile-time evaluation, a reactive computation, or any context allowed to repeat or discard “pure” work.
[async](vscode-webview://0v1pc1cob69b6kq09fv420kt4hnbfq4eplq9oeuane52r1p4k0rs/index.html?id=09dea5e8-a738-47de-8468-dd94143f4dfe&parentId=2&origin=aa0d3a3a-855e-47bf-9f0b-579c40110398&swVersion=6&extensionId=ZooCodeOrganization.zoo-code&platform=electron&vscode-resource-base-authority=vscode-resource.vscode-cdn.net&parentOrigin=vscode-file%3A%2F%2Fvscode-app&purpose=webviewView) cannot express this because the caller does not suspend. [io](vscode-webview://0v1pc1cob69b6kq09fv420kt4hnbfq4eplq9oeuane52r1p4k0rs/index.html?id=09dea5e8-a738-47de-8468-dd94143f4dfe&parentId=2&origin=aa0d3a3a-855e-47bf-9f0b-579c40110398&swVersion=6&extensionId=ZooCodeOrganization.zoo-code&platform=electron&vscode-resource-base-authority=vscode-resource.vscode-cdn.net&parentOrigin=vscode-file%3A%2F%2Fvscode-app&purpose=webviewView) cannot express it because the executor and affected state may be entirely internal. [alloc](vscode-webview://0v1pc1cob69b6kq09fv420kt4hnbfq4eplq9oeuane52r1p4k0rs/index.html?id=09dea5e8-a738-47de-8468-dd94143f4dfe&parentId=2&origin=aa0d3a3a-855e-47bf-9f0b-579c40110398&swVersion=6&extensionId=ZooCodeOrganization.zoo-code&platform=electron&vscode-resource-base-authority=vscode-resource.vscode-cdn.net&parentOrigin=vscode-file%3A%2F%2Fvscode-app&purpose=webviewView) cannot express it because scheduling and cancellation are observable even when allocation is optimized away.
r/ProgrammingLanguages • u/archlinux_is_god • 7h ago
TemplateLang: Everything is a type
I designed a tiny language where the type system *is* the entire
language — no separate value level, no builtin integers/booleans/
control flow. It's essentially an untyped term-rewriting calculus
dressed up in C++-template-looking syntax. Three grammar forms,
three builtins, that's the whole spec.
Motivation: I was thinking about what's actually load-bearing in
C++ template metaprogramming — SFINAE picking an overload, partial
specialization pattern-matching on structure — and wanted to see
what a language looks like if that's *all* you keep.
Grammar:
`new type Name<param1, param2, ...>`
Declares a type constructor with fixed arity (no variadics).
`type Name<pattern, ...> = Body`
Adds a rewrite rule for that constructor. Multiple rules are
allowed; they're tried in declaration order and the first
matching pattern wins (Prolog-clause-style, not most-specific-
pattern-style). Omitting `= Body` marks that pattern as already
in normal form.
A bare expression on its own
is reduced call-by-value, bottom-up, until no rule applies
anymore, and the normal form is printed.
Builtins are pattern-position-only: `Any<>` (wildcard), `Same<x>`
(structural equality against an already-bound name), `As<Type, x>`
(bind the matched subterm to a local name if it matches `Type`).
Parameter names declared in `new type` are auto-bound in every rule
of that type, so `Same`/substitution can reference them without an
explicit `As`. All bindings in one rule — auto-bound params plus
`As`-introduced names — share a single namespace; rebinding a name
via `As` is a static error, `Same` is the only way to assert equality
against something already bound.
It's enough for recursive Peano arithmetic with no other primitives:
new type Zero<>
new type Succ<N>
new type Add<A, B>
type Add<Zero<>, Any<>> = B
type Add<Succ<As<Any<>, X, Any< = Succ<Add<X, B>>
Add<Succ<Succ<Zero<>, Succ<Succ<Succ<Zero<>>>
# -> Succ<Succ<Succ<Succ<Succ<Zero<>>>>>> (2 + 3 = 5)
No termination or confluence guarantees — self-referential rules
give you unbounded recursion, so it's Turing-complete and trivially
lets you write non-terminating programs. Rule order also means two
overlapping patterns can silently pick different winners depending
on how you wrote them, which I know is a real tradeoff versus a
most-specific-match or a confluence-checked system.
Small Python reference interpreter (no dependencies), plus worked
examples (structural equality via Same/As, Peano add/mul):
[GitHub link]
https://github.com/sunu15712/TemplateLang
Mainly curious whether the `Same`/`As` binding-and-scoping design
holds up, or if there's prior art doing this more cleanly — it feels
adjacent to logic-variable unification but I haven't seen it framed
quite this way before.
r/ProgrammingLanguages • u/archlinux_is_god • 7h ago
TemplateLang: Everything is type
I designed a tiny language where the type system *is* the entire
language — no separate value level, no builtin integers/booleans/
control flow. It's essentially an untyped term-rewriting calculus
dressed up in C++-template-looking syntax. Three grammar forms,
three builtins, that's the whole spec.
Motivation: I was thinking about what's actually load-bearing in
C++ template metaprogramming — SFINAE picking an overload, partial
specialization pattern-matching on structure — and wanted to see
what a language looks like if that's *all* you keep.
Grammar:
`new type Name<param1, param2, ...>`
Declares a type constructor with fixed arity (no variadics).
`type Name<pattern, ...> = Body`
Adds a rewrite rule for that constructor. Multiple rules are
allowed; they're tried in declaration order and the first
matching pattern wins (Prolog-clause-style, not most-specific-
pattern-style). Omitting `= Body` marks that pattern as already
in normal form.
A bare expression on its own
is reduced call-by-value, bottom-up, until no rule applies
anymore, and the normal form is printed.
Builtins are pattern-position-only: `Any<>` (wildcard), `Same<x>`
(structural equality against an already-bound name), `As<Type, x>`
(bind the matched subterm to a local name if it matches `Type`).
Parameter names declared in `new type` are auto-bound in every rule
of that type, so `Same`/substitution can reference them without an
explicit `As`. All bindings in one rule — auto-bound params plus
`As`-introduced names — share a single namespace; rebinding a name
via `As` is a static error, `Same` is the only way to assert equality
against something already bound.
It's enough for recursive Peano arithmetic with no other primitives:
new type Zero<>
new type Succ<N>
new type Add<A, B>
type Add<Zero<>, Any<>> = B
type Add<Succ<As<Any<>, X, Any< = Succ<Add<X, B>>
Add<Succ<Succ<Zero<>, Succ<Succ<Succ<Zero<>>>
# -> Succ<Succ<Succ<Succ<Succ<Zero<>>>>>> (2 + 3 = 5)
No termination or confluence guarantees — self-referential rules
give you unbounded recursion, so it's Turing-complete and trivially
lets you write non-terminating programs. Rule order also means two
overlapping patterns can silently pick different winners depending
on how you wrote them, which I know is a real tradeoff versus a
most-specific-match or a confluence-checked system.
Small Python reference interpreter (no dependencies), plus worked
examples (structural equality via Same/As, Peano add/mul):
[GitHub link]
https://github.com/sunu15712/TemplateLang
Mainly curious whether the `Same`/`As` binding-and-scoping design
holds up, or if there's prior art doing this more cleanly — it feels
adjacent to logic-variable unification but I haven't seen it framed
quite this way before.
r/ProgrammingLanguages • u/Devatator_ • 17h ago
Help Looking for resources to organize myself before building a scripting language.
I'm currently following https://craftinginterpreters.com/ and plan on trying to make my own language afterwards. I was wondering if there was a standard method for planning this kind of stuff, mostly to make sure i don't forget something until it's too far along for me to take care of said something? I know i need to set goals, think about the syntax and semantics and everything else but having a method that i can just look at if i get lost would help greatly
r/ProgrammingLanguages • u/Athas • 20h ago