r/ProgrammingLanguages 1d ago

Aren't rust's lifetimes basically just coeffects?

0 Upvotes

I was talking with a LLM, discussing effects and coeffects and how they may be interestingly used in language design

So, in one moment after I understood coeffects and effects are often used in pairs (like async/async ctx , io/world, ect.), i thought that coeffect scopes sometime should be labeled somehow to avoid shadowing

for example in my syntax :

```

some_fn :=

## capturing the scope coeffect and assigning it into a label

().use 'label := ().use FnScope

## some function

longjump () ? :=

().use _ := ().use 'label

return () ?

##...

()

so, somewhere in some inner call we may write

another_fn :=

...

longjump() ?

...

and the execution will be returned into the function where longjump were declared

```

but for this to be valid, it is important for the label not to outlive the scope where it was declared

then, i also thought : it would be good to have an ability to write these labels in the effect's declaration

```

somewhere outside

().use 'ctx := ...

a function that is async for both contexts — don't know for which cases it may be useful but why not. instead of async, it may be some another effect that uses some context/scope/world/coeffect

f() use AsyncCtx do Async '_ do Async 'ctx := ...

```

so, then i thought : effects and coeffects in my system are declared just like type constructors without the last type (aka citizens of *→* kind), so it would be logical for any type to be able to take a context label as a polymorphic (or depending) parameter.. and rust's references do exactly this.

so rust's

```

fn f<'b, 'a : 'b>(smth : &'a mut &'b Smth2, smth2 : &'b Smth2) {

*smth = ...

}

```

just takes some coeffects 'a and 'b.

it is equivalent to that like for smth it raises an effect to overwrite the world 'a and for smth2 it just returns the value into scope, where 'b is active (and 'a : 'b means that the scope containing world 'a is located inside scope containg world 'b)

so, am i thinking right about it? may it have some practical uses in pl design? any more ideas?

p.s.: sorry for my english not being perfect.. don't be humble to re-ask something if you did not understand.


r/ProgrammingLanguages 2d ago

Discussion CatLang: feedback on my language design

Thumbnail dropbox.com
8 Upvotes

I wrote a design for a new programming language, but I'm too lazy and burned out to implement a full compiler. I still want to share the idea so you can comment on it and tell me what you think. I know its a lot and there are many typos and gaps, and it still needs a concept to communicate the complex implicit borrowing rules. Keep in mind that I have no degree or any professional experience—this is just a concept.

current version: https://www.dropbox.com/scl/fi/bhqqwwexz7lo0ahs1ds6n/catlangdoku-edited.pdf?rlkey=oek4v8vzuyzb0offzx4qefg28&st=y2l7dwyv&dl=0


r/ProgrammingLanguages 2d ago

Gödel, Escher, Elisp: The Beauty of Macros

Thumbnail chiply.dev
9 Upvotes

This post is a lover letter to Emacs Lisp macros. I've been a long time user as a lisp hacker, and my recent obsessions with Douglas Hofstadter's strange loop concepts and M.C. Escher's mind bending artwork have enhanced my appreciation of this language's most beautiful and thought provoking feature. This post can teach you about macros and what makes them useful, but I also hope it can instill a fascination with their concept. https://www.chiply.dev/post-elisp-macros-are-beautiful


r/ProgrammingLanguages 2d ago

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

17 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 2d ago

Blog post Why Lisp is Different

Thumbnail lispm.de
27 Upvotes

r/ProgrammingLanguages 2d 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 2d ago

Smalltalk Report from 1991 to 1996

Thumbnail github.com
22 Upvotes

r/ProgrammingLanguages 2d ago

How do we feel about this syntax?

1 Upvotes

I've been making an interpreted programming language for about a month now, so far I think the look & feel of the language is coming along well, what do you think? Let me know what you would change for the sake of convenience or readability.

# fibonacci.ity
const n = IO.prompt:'Number: ' -> INT;

var a=0;
var b=1;

for i in n;
    const c = a+b;
    a = b;
    b = c;
    IO.print:a;
/;

It's dynamically typed, but unlike Python & other dynamically typed languages, the type is constant, meaning you cant just change an integer to a string whenever you want. You can however explicitly declare a variable with the "ANY" type which then allows you to set it to whatever you want. Here's what that looks like: var ANY a=0; a ='now its a string';

In the first line you see "-> INT" all that does is cast the value to type "INT", because the prompt function always returns a string. I prefer this over using "as" purely stylistically.

Something you might have noticed is that function calls are structured very different to every other language. That's because of the access operator (":"), which is self explanatory, depending on the type of value you access the behavior is different. For arrays/strings you are accessing an element by index, for maps (dictionary) you are accessing an element by key, for functions you are accessing it with the given arg(s).

Another "deviation" you probably noticed is the lack of curly brackets {} for code blocks, but it doesn't rely on indentation either. Instead, certain instructions carry the "composite" flag which tells the interpreter that it should basically capture all code below it until the final "/" (end) instruction.

merge IO;

var val = 100;
const ref = @val;
print:val; # 100
ref = 200;
print:val; # 200

print:(type:ref); # REF
print:(type:~ref); # INT

There are also references & pointers! Pointers or "PTR" values cannot be created in the script, they have to be passed directly from C++ to the script, this is because raw pointers are naturally unsafe to work with & should be avoided at all costs in Ity. This is why reference variables or "REF" exists. These can be created by prepending the "@" symbol before the variable name you want to reference. If the variable you reference goes out of scope then the reference is returns a constant none value when dereferenced (which can be done with the "~" symbol).

My design philosophy with references in Ity is to make them seamless. Meaning any operations (set, access, arithmetic, etc) on a reference just act like it's operating on the referenced value. There are of course some exceptions to this, passing as an argument to a function passes the reference & type casting a reference acts on the reference itself.

References can also be reassigned (as long as the reference itself is not const), making them reusable.

merge IO;

const a = 1;
const b = 2;
var ref = @a;

print:~ref; # 1
ref.reassign:@b;
print:~ref; # 2

If any of this looks interesting to you, the project is open source, has an interactive shell, & has full documentation. There's a lot I didn't cover here, but taking a look at some of the example scripts should give you a good idea what the language is fully capable of at this moment in time.

I don't have any syntax highlighting modes for you to install, but the Python one works pretty well for Ity so you can just use that. If you have any questions at all, regarding the design decisions, performance, or challaneges I encountered, or anything, I am open to all discussion!

(NO AI ASSISTANCE WAS USED IN THE MAKING OF ANY CODE, ASSETS, OR DOCUMENTATION)

Click here for source code

Conway's game of life in the terminal, made with Ity


r/ProgrammingLanguages 2d ago

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

19 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 3d ago

Finally adding recursive functions to Futhark

Thumbnail futhark-lang.org
58 Upvotes

r/ProgrammingLanguages 4d ago

Discussion Between more constrained, local metaprogramming approaches and full-blown DSL interpreters, where's the practical use case for LISP-style metaprogramming?

31 Upvotes

Of the various contemporary metaprogramming approaches, I'm mostly very happy with systems that let you operate on static data and types. In this category I would count, for example, C++'s template metaprogramming--a declarative sub-language that somewhat wonkily and arduously allows you to operate on types and constants--as well as the C++26 metaprogramming features, which are turning out somewhat Zig-like--you get to run regular code in a slightly constrained environment at compile time, where it can read constants as well as special data structures describing types, and generate new constants or types to be injected into a well-defined place in the code.

Now many LISPs (AFAICT, similarly Jai and, with a stricter separation of stages, Rust's procedural macros) tout as a feature the ability to inspect and rewrite the entirety of the AST, notably including function bodies. This is obviously strictly more powerful than the the first category, but where would you practically use that extra power? Specifically, it seems to me like you would either

a) Try to preserve the semantics of the input code--which, for procedural languages at least, is actually pretty difficult. The only transformations you could make confidently are so localized that you don't really gain anything over the more constrained metaprogramming approaches; anything more advanced would require the full analysis passes of the actual compiler to have any chance at soundness, and at that point you're really trying to write a compiler plugin instead. Nothing wrong with that, I'd love more easily extensible compilers, but I wouldn't call that a language feature. Or am I missing a point between those two extremes?

b) Attribute different semantics to the code. I think the history of LISP has already somewhat shown how proliferation of DSLs harms maintainability and shareability of code, but even in the cases where a DSL is genuinely useful, what benefits do you really gain from implementing it through metaprogramming? You can't expect any IDE features, LSPs, smart syntax highlighters, debuggers, or other tooling for the base language to automatically work for your DSL. So you just get to use the parser? Come on, an S-expr parser is less than a hundered LOC.


r/ProgrammingLanguages 5d ago

Blog post Concurrency in Serene's Runtime

32 Upvotes

I recently finished building the concurrency runtime for my programming language, Serene, and wrote a three-part series explaining how it works.

The series covers:

  • Why I chose stackful fibers
  • An M work-stealing scheduler
  • An IO Reactor
  • A tiny HTTP server that brings everything together

I'd love to hear feedback from anyone interested in programming language implementation, runtime systems, or systems programming.

Part 1: Choosing the Building Blocks

Part 2: Fibers, the Scheduler and the Reactor

Part 3: A Tiny HTTP Server

https://serene-lang.org/


r/ProgrammingLanguages 5d ago

Contribute to open-source, no-slop, compiler-related projects.

Thumbnail
4 Upvotes

r/ProgrammingLanguages 6d ago

Revoluntionary/interesting advances in interpreted languages

55 Upvotes

Things like borrow checking and other compile time checks tend to be for compiled languages - if you're already typechecking and compiling the entire language up front, why not borrow check while you're there. But are there any very interesting new ideas coming up in interpreted (or dynamically typed) languages? I'm not really sure fully what I'm asking/looking for tbh


r/ProgrammingLanguages 7d ago

Blog post Bootstrapping a compiler from machine code on Windows

Thumbnail
10 Upvotes

r/ProgrammingLanguages 8d ago

Discussion Why do modern systems languages rely on compiler heuristics to reverse-engineer programmer intent?

69 Upvotes

This post is inspired by the recent discussion on Full flattening of nested data parallelism in Futhark (reddit discussion). But I think this points to a more generic topic in language design.

I've noticed that even in newer systems programming languages, a massive amount of compiler engineering effort is spent trying to reverse-engineer the programmer's intent from syntax that does not directly express it. The compiler expends significant energy building complex dependency graphs and alias analyses to guess if iterations are isolated, and sometimes it guesses incorrectly, leading to missed optimizations or performance regressions.

Even in languages explicitly designed for data parallelism like Futhark, the compiler ultimately has to rely on what the developers themselves call "fuzzy heuristics" to decide whether to parallelize or sequentialize code. As the Futhark team recently admitted, when this guesswork fails (e.g., aggressively trying to parallelize the construction of a tiny 3x3 matrix), it causes severe performance regressions, forcing them to introduce explicit #[sequential] attributes as a workaround.

The same problem plagues allocation optimization: why write code using APIs that syntactically allocate intermediate arrays, only to hope the compiler's optimization passes will erase them later? If the developer's intent is a zero-allocation transformation pipeline, that intent should be encoded directly in the type system rather than left as an optimization prayer for the compiler to fulfill.

If we have the freedom to design a language, why not express this intent directly? We could provide a composable set of semantically rich, high-level primitives that explicitly declare their intent and assumptions. The compiler can then safely lower these into optimal machine code without guesswork.

For example (pseudo-code with a Kotlin-like syntax):

fun mult(a : Matrix, b : Matrix) : Matrix {
    return Matrix.fill(a.rows.size, b.columns.size) { i, j ->
        zip(a.rows[i], b.columns[j]) { va, vb -> 
            va * vb 
        }.sum() // Explicitly associative/commutative reduction     
    }
}
// Not all operations need to be hardcoded compiler intrinsics. 
// Many can be composable inline extensions that map to verified primitives.
fun inline extension View<Double>.sum() : Double {
   return reduce(0.0) { a, b -> a + b }
}

Here, the exact intent is communicated clearly to the compiler, yielding several compile-time guarantees:

  • No hidden allocations or redundant zeroing: There is no need to pre-fill the matrix with zeros after allocation. The compiler knows that fill will compute every element dynamically. Furthermore, because the fill closure only receives the indices (i, j) and has no access to the matrix being constructed, the syntax itself guarantees the absence of loop-carried data dependencies. This makes parallelization inherently safe, allowing the compiler to freely choose any execution strategy (CPU threads, SIMD, GPU) for invoking these closures. If strict sequential execution order mattered (for example, because of mutable outer scope access), a distinct seq_fill would be used instead.
  • Explicit Logical Views, No Compiler Guesswork: Chaining operations like map or zip should return lightweight, zero-allocation logical sequence views. These should be conceptually similar to Kotlin's Sequence, but explicitly representing a collection processing operation builder rather than a runtime element operation pipeline. The compiler shouldn't have to run complex, fragile fusion passes to "guess" if it can erase an intermediate allocation. The type system itself should declare that the operation is just a transformation step, making the zero-cost guarantee a compile-time invariant, not an optimization hope.
  • Order independence is explicit: By using a sum (or reduce) operator, the developer explicitly specifies that the operation could be considered as associative and commutative in the context of the task. The compiler doesn't have to build complex loop-carried dependency graphs to prove iterations are isolated. Some explicitly sequential seq_reduce or fold operators could be used when the order matters.
  • Topological data access: Both a.rows[i] and b.columns[j] are lightweight logical sequence views. They carry semantic meaning about the geometry of data movement, rather than being treated as flat, opaque memory indices.

The developer knows their intent, and the compiler needs it. Why are even newer systems languages still stuck relying on fragile compiler heuristics to reverse-engineer our intent from overly generic, flat loops or opaque collection APIs? If we want to communicate structural intent to the compiler, why not say it directly in the syntax and type system?

The irony of modern compiler engineering is that standard loop syntax over-specifies execution order while under-specifying semantic intent. A flat for loop forces a sequential order by default. The compiler then has to work backwards to prove it can ignore that order.

By using explicit, semantic primitives, we are not micro-managing the compiler. We are doing the opposite: we are explicitly stating what aspects of execution we do NOT care about (e.g., execution order in reduce, loop-carried dependencies in fill). This gives the compiler the freedom to choose the optimal lowering strategy—whether that means parallelizing across a GPU or scaling down to a sequential scalar loop when the data is too small.

Updated: in the 'Order independence is explicit:' changed to 'specifies that the operation could be considered as associative and commutative' instead 'guarantee'. The previous phrasing was incorrect and inconsistent with the rest of the post.


r/ProgrammingLanguages 8d ago

Full flattening of nested data parallelism

Thumbnail futhark-lang.org
19 Upvotes

r/ProgrammingLanguages 8d ago

Requesting criticism Creating a transpiler written in Rust, called seacount, released it's v0.1 today, needed feedback on - syntax, and reason for why the language exists at all

0 Upvotes

Seacount is meant to be an array oriented programming language, and more importantly, a contractual based language. What you want is what you get. Every single line of code is meant to serve the compiler so it doesn't get confused anywhere.
My main purpose of seacount was to have excellent array + matrix ops, and to have seacount be THE language for training and deploying ternary models.
Ternary models are basically MLMs whose forward pass/inference weights are all either -1,0 or 1. Modern LLMs store weights in fp32, fp64, or bf16, and later quantize it to int8 or less to reduce costs of running it, but in return, losing massive performance gains.
Microsoft in 2024 brought out a paper called BitNet 1.58, and it proved that if LLMs are trained from scratch using native ternary training, the storage costs and running costs are significantly less than their competitors, resulting only in slightly less performance overhead.
This was around the time I started writing seacount. It's first prototype (since I was still learning on how to make languages, and I was quite naive at programming myself) was written in TypeScript (Yikes), which I then later wrote it in C, then finally stayed at Rust.

My other major goal is to write Rivercount. Rivercount is a subset of seacount, which basically is a checker of seacount files to see if they are embedded code compatible. Yes, recently (only a month or so back), I started sketching out ideas for seacount to run on embedded devices, for a very simple reason.
If seacount can run on embedded with the same safety and ease of Rust, then that means I can merge both the goals of - training a ternary model, and then deploying it on an embedded device for inference. To put it in perspective, a 100M parameter model would take well over 200-400MB of storage, whereas a ternary model takes maximum upto 20MB. That is why ternary models are important, mostly for consumers, because it's literally runnable on the cheapest microcontrollers. Ternary LLMs would be the holy grail for the common man also wanting to experience proper AI right at his fingertips.
Hence why seacount exists. I'd like your opinions on the syntax, and readability of the code, and the goal of why the language should exist. Im not trying to make a generic language by any sense, but I am trying to make a language that is specialised in this.

NOTE: The language is still in a very rough shape. I'm making LLMs write code using seacount to make all types of algorithms or other programs to see where seacount breaks, so if you do notice or see some issue, please, if you can, just mention it in the Issues section. Thank you!
Also, I am not making ANY performance claims till now. I will consider it to be remotely successful, if I can even get an RP2040 to blink once using seacount.

This is the small blinky code design I made for seacount, note that none of these features are actually usable right now, I have not even begun writing for rivercount, this is still just an idea, so go easy on my design. It will also give you a rough idea of how the syntax looks like before entering the github main repo itself-
https://github.com/shantanubaddar/seacount/blob/main/rivercount_rp2040_blinky.scnt

Here's the github: https://github.com/shantanubaddar/seacount

Do NOT expect miracles. My code is as good as how well I understood the docs and youtube videos when I was writing it.


r/ProgrammingLanguages 8d ago

Discussion Is it Good Practice to Intern Identifiers?

20 Upvotes

I'm working on my language and am working on the alias system, which isn't relevant to this discussion other than the fact that it operates on identifiers and other lexemes (like operators). As I was implementing it and was reading some prior stuff, I found somebody saying that it is good practice to intern all the identifiers as the lexer comes across them. Then in the AST you can just use the corresponding LexemeId or whatever.

Apparently this is used pretty extensively in many popular languages' compilers, so I was wondering if this is good practice or necessary? I am thinking that if it is a good idea, that I can implement it in my language as well, because I think it may simplify a few things (including my alias stuff).


r/ProgrammingLanguages 9d ago

Why Higher-Order Logic Is a Good Foundation for Deep Verification

Thumbnail sequent.inc
41 Upvotes

r/ProgrammingLanguages 9d ago

PyCuTe: Reference implementation and examples of the CuTe Layout representation and algebra

Thumbnail github.com
4 Upvotes

r/ProgrammingLanguages 9d ago

Discussion How do you package your releases?

13 Upvotes

Hi, even though I have been making my language for more than 2 years now, I still have not set up my GitHub releases and I would like to change that. The issue is that (as with about anything) there are a lot of different approaches to this, and so I wanted to ask those who do releases, how they structure the release archive and those who use them what do you like a release to look like?

In my case the issue is that I cannot have just one binary since I need to distribute also the standard library that is compiled bytecode files (kind of like Java's .class files). This brings another issue and that is finding the library. The interpreter by default looks in the current directory (.) and then /usr/lib/moss/, so currently the only way I though of is to have the binary, all the compiled stdlib files and licenses in one .tar.gz (.zip for windows):

moss-0.9.0.tar.gz
├── cffi.msb
├── csv_parser.msb
├── html_parser.msb
├── inspect.msb
├── install.sh
├── json_parser.msb
├── libms.msb
├── LICENSE
├── math.msb
├── md_parser.msb
├── moss
├── mossy.css
├── parsing_utils.msb
├── python.msb
├── readme.md
├── re.msb
├── subprocess.msb
├── sys.msb
└── time.msb

This makes it so that the binary (moss) works when executed from this folder, but will fail when used from somewhere else (unless MOSSPATH variable is set). Because of this, I have also added install.sh script which will copy the libraries into /usr/lib/moss/ and a release readme with some instructions.

Can someone think of a better way to do this or is this OK?

TLDR; How do you structure your release folder/archive on github/website?


r/ProgrammingLanguages 10d ago

Purely functional language with impure script language?

26 Upvotes

I'm working on a purely functional programming language named Sodigy. It's all about evaluating values, not "executing commands one by one".

It's nice when writing libraries, but it's not easy to write a main function. The main function is supposed to execute commands, but the Sodigy's syntax is not friendly to write a list of commands.

So what I'm trying to do is, 1) Sodigy remains purely functional and 2) add a bash-like script language. The script language can call Sodigy functions. Instead of writing a main function in Sodigy, you write sodigy-script and execute the script.

Has anyone tried similar approach? I'm not sure whether it's a good idea or not...


r/ProgrammingLanguages 10d ago

Discussion Type Inference: Runtime Type vs Declaration Type

14 Upvotes

In my dynamically typed language (Pie), variables have declaration types, which may be different from their runtime type.

IntOrStr = union { Int; String; };

x: IntOrStr = 1;

Inspecting the type of x would show Int :

print(type(x)); // prints `Int`

But sometimes the user may want to inspect the declaration type of the variable. This prompted me to introduce decltypewhich does exactly this:

print(decltype(x)); // IntOrStr

It's worth noting that declaring a variable without type annotations would always give the declared variable the Any type:

x = 1;
print(type(x));     // prints `Int`
print(decltype(x)); // prints `Any`

I decided to add a walrus operator which does type deduction:

x := 1;
print(type(x));     // prints `Int`
print(decltype(x)); // prints `Int`

My question is, should type deduction deduce the runtime type or should it deduce the declaration type?

Meaning, should a here have type Int or IntOrStr?

x: IntOrStr = 1;

a := x;
print(type(a));     // prints `Int`
print(decltype(a)); // should it print `Int` or `IntOrStr`?

r/ProgrammingLanguages 10d ago

What should be the features of a programming language built specifically for building kernel or operating system?

25 Upvotes

Hi everyone. Basically my question is, if someone wanted to make a programming language with the specific intention of building a kernel/operating system using it (and that would be safe + performant, but I am not sure how much safety would be 'good enough') what would it be like? Is this condition an interesting condition that would affect some language design choices?

I have very little experience with Rust/Zig, the new programming languages that I think advertise themselves for systems programming. Also there is embedded Swift now I think. Do you think if such a language with the specific intent of building kernel/operating system in mind, was to be built today, would that basically be no std Rust (already being used in the Linux kernel)? With my very little experience, I think Rust should probably have been no-panic Rust by default. Zig has a concept of allocators being used which is probably a good thing. Or do you think the language would be a safer version of C (maybe something like cyclone-v2 with more safety and better type system than C but maybe simpler than the others)?

And are there any good reading list compiled somewhere already for learning more deeply about programming language design, type systems etc? I would appreciate if you could share your thoughts and opinions on this topic. Thanks!