Copper – A minimal low-level programming language with OOP and generics
tibleiz.net
tibleiz.net
For example, variable declarations use the pattern <colon> <type>, as in
var a: Int32
But literals follow the opposite syntax, as in a = Int32: 42
The return type of a function is introduced by : in func definitions, but by -> in imported func declarations: func foo(a:Int):Int
import func bar(a:Int) -> (Int)
Other construct that I find far from my habits: var w, h, d = 640, 480, 2
instead of the more obvious (for me): var w=640, h=480, d=2
Arguments are passed by ref, not by val. Good luck with recursion!Agreed on the rest, though I don't understand what the problem would be with recursion. Is it using reference passing also for primitives like integers?
That's what I assumed, but honestly I might have misunderstood the docs. This passage is not very clear:
> Copper manipulates only references to objects. Simple data types such as integers must be seen as references to stateless objects (i.e. objects that have no state in memory).
Import functions is done the same way:
func foo(a: Int): Int
import func foo(Int): Int
May be the doc is outdated.The multiple declaration is consistent with assignment:
var w, h = 640, 480
w, h = 640, 680
w, h = getBoundingBox(obj)
The syntax var a = Int: 32
is unusual compared to many other languages because it just use a simple syntax: var id = expr
The type is prefixed only to resolve literals: var a = Int32: 42
var b = Uint8: 42
It can be useful in generic functions: println("The answer is \a", Float32: 42)
But to create uninitialized variables I had to keep the syntax: var id: Type class UserId: Int64
...
end
var user = UserId: 42
I also plan to replace predefined Int64, Int32, ... by generic types Int(64), Int(32), ... LLVM allows any integer size and Copper allows this kind of generic types.Same for function types? The docs still list the syntax with "->":
& (Type) -> (RetType) & (Type) -> (RetType1, RetType2)Then again, I'm the kind of person who've chosen $'.\U0001F9CC' (that is, $'.\xF0\x9F\xA7\x8C') for a file extension of my own toy language so...
Programming to an abstract interface — and in large codebases, enforced visibility controls — represent the middle ground everyone wants, in my experience, between “OOP” and “sir, this is C”.
The example definitely shines. Doing something barely non-trivial is great since it gives a feeling for the language. I wish other language introduction pages would take note.
It does feel like you need some shared understanding with the author about what the interesting parts of a programming language are and what can be left implicit.
Perhaps the author and I have similar intuitions? I guess I also like that isn't pure LLM output style. It was refreshing to see some human work.
Yep. Interface-based (true) delegation between objects would be my go-to if it existed.
What's the difference between an exemplar and an example?
between “OOP” and “sir, this is C”.
That's more templates, ownership and operator overloading.
Polymorphism is not a modern way to program because it introduces dependencies and enormous speed hits from pointer chasing, both of which are corrosive to a good program.
So in a programming text for a new language, one would use examples of things that are strings, or examples of structures one might declare. You might even see examples of multiple ways to solve a problem in code. An exemplar though shows the one best - or at least most recommended - way to do something in the language.
Why would you use a worse example and how do you know the example you are looking at is the best?
It can be difficult to know if your example is the exemplar. It can be reasonably assumed that if the creator of the language provides only one example, it at least should be the best one. It is at least the best one in the docs if it's the only one.
What is the memory model here? What does it compile to? What makes it 'low level'? Is there a GC? Does it have ABI compatibility with C or a FFI?
Also really interesting are the author's blog with articles on language design (for example his thoughts on exceptions, and how he added them to his language, then decided against them), and the change log for the language, showing its evolution.
> The language is not object oriented but it provides some features to facilitate object oriented designs.
It has classes and implicit `self`. Not nothing, but I don't think it's incorrect to say has any more OOP than general purpose languages like Lua or JavaScript in that sense.
Something like Pony[1] is a more mature and feature rich entry and has a much better OOP story.
All that said: a) I'm sorry for doing the thing I hate when other people do
b) Creating a language is a big undertaking, and it's super cool that they did it
c) I see it seems to already be written in Copper too, which is also very neat!
There's a difference between nitpicking the topic of the article and nitpicking someone's grammar.
IMO, that text is confusing.
“Not nothing” is saying it has some OO features.
“I don't think it's incorrect to say has any more OOP than general purpose languages like Lua or JavaScript in that sense” = I think this language has more OOP than languages like Lua or JavaScript”
So, that more or less says “A, but A”. If so, what’s the “but” about?
I had retyped it a few times trying not to come off as overly negative or a jerk.
The comment is saying that the language has more OOP than Lua or JavaScript, but it seems like it might have been meant to say the opposite. That, and it appears to be missing a subject (which I assume to be ‘it’ or ‘the language in question’, based on context). So personally I do find it somewhat confusing.
The problem is not the double negative itself, though; it’s more how it’s being used in context here.
As long as:
* Your language is the kind of language that suits compiler development.
and
* You support compiling to C or something so that bootstrapping doesn't involve an opaque binary or compiling 30 different git commits in a row.
What are the downsides exactly?
Since the majority of programs are not compilers, it might make more sense to focus on interesting things that aren’t relevant to compilers.
Some ideas from the top of my head: async I/O, a great SIMD story, effect systems, robustness systems for long-running processes (I’m only aware of Erlang/Elixir), and many others.
Compilers do exercise some things, like error handling and memory management, but they’re not particularly difficult or interesting in general.
That said if your languages is for building compilers you'd probably want to do it. Otherwise, basically no one writes compilers, and if they are they're writing them for their own languages in something much more mature than your nascent language. And if the ambient advice is to bootstrap asap, they'll only be using your language for a hot second anyway, so they're the worst users to cater towards.
These days I think all languages should try to do it with an LLM because, why not? That also means bootstrapping a compiler isn't exactly an impressive feat anymore, most new languages are doing it now.
The problem happens when you have bugs and test failures. The hoster and hostee look the same, behave the same, and emit the same error messages. So I had unknowingly opted into spending a big chunk of my debugging time being confused and wrong about A) where errors were coming from and B) what they even signified.
Basically, self-hosting too early, before the thing was stable and trustworthy, was a disaster. (It's obvious in retrospect, really, but was still pretty awful at the time.) I tried again later, after everything had stabilized and it was routinely building other projects routinely, and it was fine.
Bootstrapping is a one-time cost, but implementing the toolchain in C is a cost everyone has to pay every single time they touch the toolchain source.
I’m sure there are benefits to a C toolchain, but the costs overwhelmingly favor self-hosting for many languages.
I agree about your dogfooding comment though.
And even though I like writing C, the build tooling and the lack of modules system and a bunch of other footguns remain. (And say what you will about language-specific build tools, but they work very well for 99% of cases, and the benefits of the entire ecosystem using the same tool are significant).
Moreover, I want contributors to my language, and the reality is that however much I like C, the intersection of people who want to work on my language and people who like writing C is going to be vanishingly small. If my language is C-like (e.g., Zig) then that intersection will likely be much bigger, but as the target language approaches C, the benefit of using C goes to 0.
I agree that there exist many good reasons to make a language self-hosting. Still, I think there also many obvious benefits from using C.
Given the choice between a language that requires expert coding for minimal safety guarantees and one which can tolerate human error, I’ll take the latter all else equal. Also, given that experts feel they can’t write correct C reliably, I’m inclined not to think I can (never mind contributors).
> At the same time, the Rust rewrite of coreutils with more than 100 CVEs does not exactly convince me that the problem is mainly the choice of language.
I’m not a Rust fanboy, but I don’t know what I should make of this. Is 100 CVEs a lot for coreutils? If the idea is “Rust doesn’t preclude all CVEs therefore language choice doesn’t matter”, I don’t think that follows.
> Still, I think there also many obvious benefits from using C.
The only I can think of are that the toolchain—but not the compiled programs—-can run on niche hardware (minor benefit), that bootstrapping may be easier, and that the toolchain may be more performant (although Go compiles faster than most C-based toolchains I’ve seen, so I’m inclined to think the language matters less here than the compiler design itself).
Again, I do not believe this is true. One gets a reasonable level of safety with only basic measures even in C. In practice, memory safety is also not even remotely the main issue to worry about, especially when writing a compiler.
> Also, given that experts feel they can’t write correct C reliably, I’m inclined not to think I can (never mind contributors).
It is not easy to write correct code in any language. See the number of CVEs in the Rust rewrite of coreutils or generally the number of issues one also sees in Rust projects (including still memory safety issues.) If at all, this means that the C experts are not naive about it.
Even if you believe this is true and the experts are wrong, other languages don’t require even these “basic levels” to provide a greater degree of safety, so presumably they’re still the better choice if only for this reason (not to mention all the others).
> In practice, memory safety is also not even remotely the main issue to worry about, especially when writing a compiler.
I agree—but those other concerns (e.g., attracting developers) are overwhelmingly reasons not to use C.
> It is not easy to write correct code in any language
Right, but “impossible” and “not easy” are different, right?
> See the number of CVEs in the Rust rewrite of coreutils
How are we supposed to derive anything from this? The 44 Rust rewrite CVEs were the result of a comprehensive professional audit that Canonical paid for and which the coreutils project has never experienced. Moreover the coreutils project predates CVE trackers. If your point is “Rust doesn’t preclude logic errors”, then who are you arguing with?
> including still memory safety issues
Which of the 45 or so CVEs were memory safety?
I do not understand your comment about "impossible" and "not easy". It is not impossible to write correct code but also not easy - in any language.
The audits of Rust uutils which identifies 113 issues in total confirm exactly what I said: It is not easy to write correct code in any language, and also in Rust it is very easy to make stupid errors that can compromise safety and security.
My comment about memory safety was about Rust CVEs in general, not uutils. For example, https://rustsec.org/advisories/ lists more than 20 use-after-frees for 2026.
I think most people today would say encapsulation and message passing are pretty core to the definition of OO and inheritance is largely seen as an anti-pattern.
One thing I really like about Elegant Objects is that it is very palatable to FP enjoyers. Objects must encapsulate state, but they must also be immutable. Good OOP is declarative like good FP. It looks a lot like lisp imo. You're encouraged to code against interfaces instead of using inheritance etc.
Clade - a programming editor with elastic tabstobs and Smalltalk like navigation
Maybe, there is more. Looks to me like an illustration of the "It is a quiet life" meme
This language is a playground where I constantly add and remove features or try various experiments, so I have no intention to promote it. The only reason I have published it is that I publish an open source text editor written with this language.
After twenty years of evolution, it starts to become stable but not yet, I had recently added some basic option types.
I have this crazy idea to do a hail mary codex ralphing session to convert an IoT product to assembler so as to aggressively reduce the flash and ram usage. probably a pipe dream, but if my old mentor could build a diet planner on an 80-byte RAM PIC, galaxy-brained GPT should be able to fit my bloated product down from 1MB flash to 256kB, amiright?
Multiple return values are kind of weird, but maybe it is just me. Overall this is something I will give a try.
Neat little language...
Definitely more explicit by default though.
But yes, very neat. I'd miss Enumerable though, if they manage to get that to play with manual allocation that would be even neater.
Compiling to native is neat. But productivity (LLM-ability, lib ecosystem, will-still-be-maintained-in-20y) is key for me :)
Here's my current stab at a small compiled language: <https://codeberg.org/stevefolta/lx-2025>.
> Ruby-style syntax
Also, Python has significant whitespace, which this language does not appear to have.
That's used... to make what?
There do seem to be some Unicode constants in the standard library:
https://tibleiz.net/browser.html?path=std&title=Standard%20L...
I find that difficult to believe. Is there some objective evidence?
> like someone writing out machine code by hand vs using a compiler back in the day
Programming using a compiler is still the same kind of thing, just in a different language.
AI is undergoing cosmic inflation, we get further apart faster.
As others have proclaimed: what matters is taste. And (perhaps?) a deep understanding of the subject matter. Which may or may not be encoded in formal specifications. But may also include 'fuzzy' domain-specific knowledge that can't be scraped from the web.
> Humans don't code anymore
I do, and I'm far from alone. Whether 'product' gains traction, that's another matter. But you know what? It doesn't matter. If it works for [author], that's enough.
Uh, no.