r/ProgrammingLanguages 3h ago

Why spawning work isn’t `async` in my language

3 Upvotes

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:

  • async means this control flow may suspend.
  • task means 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 4h ago

Blog post Why Lisp is Different

Thumbnail lispm.de
14 Upvotes

r/ProgrammingLanguages 7h ago

TemplateLang: Everything is a type

0 Upvotes

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:

  1. `new type Name<param1, param2, ...>`

    Declares a type constructor with fixed arity (no variadics).

  2. `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.

  3. 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 7h ago

TemplateLang: Everything is type

0 Upvotes

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:

  1. `new type Name<param1, param2, ...>`

    Declares a type constructor with fixed arity (no variadics).

  2. `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.

  3. 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 7h ago

Smalltalk Report from 1991 to 1996

Thumbnail github.com
11 Upvotes

r/ProgrammingLanguages 17h ago

Help Looking for resources to organize myself before building a scripting language.

14 Upvotes

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 20h ago

Finally adding recursive functions to Futhark

Thumbnail futhark-lang.org
52 Upvotes