Ask HN: What features would your ideal programming language have?

11 points by msvan ↗ HN

16 comments

[ 4.8 ms ] story [ 53.4 ms ] thread
Basically Python with better anonymous functions, no GIL, and a sane consensus solution to package management and running multiple versions of the language on one system.
First-class support for a version manager so that 20 years from now, I will be able to check out my project and build it immediately using the locked platform version.

A build system that makes it super easy to do the right thing with regard to locking dependency versions.

A compiler backend that generates reasonably small binaries.

Nim, but with a sane naming convention, less dependency on the underlying C language, and stability across versions.
I want something like literate programming, documentation is enforced somehow. Or diagram can be translated into code and vice versa.
Most probably features that, in the end, contradict each other.

For example have all the nitty-gritty cool features for a low level programming language BUT also maintain Python-tier readability.

I'm really intrigued by Jai because it has many of the ideas I want to see in more languages and has pretty killer syntax. The concept of a meta program that Jonathon Blow talked about on the "On the Metal" podcast is wild.

A macro syntax that was both expressive/hygienic, but allowed for better integration with tooling (frequently tools have no introspection into expanded macro code, and debugging macros almost always sucks). I'm not sure how to solve this problem however, but I think procedural macros in Rust are a decent enough start (debugging them sucks right now, but I see no reason why that has to be the case).

Along the same lines of the previous paragraphs (programming things around the program itself, like meta programming) is that the build process in most languages sucks, and requires using outside tools in different languages (be it a config file, make script, python/shell scripting, etc). I'd really like it if I could write my entire build process in the language I'm building, including pre/post build steps, specifying a dependency tree, etc.

An idea I love to work with is a purely declarative syntax, similar to configuration languages like TOML or JSON. But inside declarations you can write imperative code that synthesizes your declarative code programmatically. Now you can do this in JS today with some discipline, and make it kinda typed in Typescript. But if you have a fairly strict and limited type system and good error messaging/debugging workflow it's actually a fairly powerful concept. It helps deal with the oscillation in the industry between "configuration is code" and "configuration is actually a bitch to write for complex systems." You can start with a pure configuration for your system, but then as complexity grows write imperative code that synthesizes the configuration.

I'd like to suggest one refinement to your idea. Being able to write the entire build process in your language is great. Having to write it in your language may be not so great. Allow the option of using alternate build systems without the build process being crippled.
The way I'd implement it is exposing the compiler/build tooling as a library and then a command line front end on top of it. So any tooling that can exec a CLI would be able to build for the language, but you could also compose a script that imports the tools as a library. The hairy ness is that you probably don't want arbitrary code execution in the build scripts themselves, I know there was the idea of executing the pre build scripts for rust projects by compiling to WASM and executing in wasmer or another sandboxed runtime to deal with that problem.

But in my ideal language you'd probably have a repl or virtual machine that could be sandboxed and used to execute the build scripts, including invoking the compiler as a library.

I think that different kind of programming languages can be suitable for different purposes, although I do have many ideas, anyways.

Some of them are:

- Macros (both hygienic and unhygienic)

- Bitwise manipulations

- Less confusing syntax for types than C has

- ASCII based (not Unicode, although you can write programs to work with data in any character set, including Unicode)

- Meta-programming

- Low-level features

- You have enough ropes to hang yourself, and also a few more just in case

- GOTO and GOSUB

- Direct dealing with the stack

- GADTs (without runtime support)

- Control over optimizations in different parts of the file

- Better namespacing

- Binary include files

I am sorry I did not write more elaboration of this. Also, I have more ideas too, but did not write them on here right now.

So a couple of questions.

In terms of control flow, why do you need GOTO, why GOSUB instead of a normal function call, and what do you mean by "direct dealing with the stack?"

This sounds a lot like a version of C++ without the cruft and C backwards compatibility. Which is a fantastic idea, and I know it's been attempted many times. Which languages out there today do you think fit your criteria the closest?

I don't want GOSUB instead of a normal function call, but in a few cases it is helpful to have it in addition to having normal function calls. In BASIC, a subroutine called by using GOSUB can RETURN either to where it was called from or to a different label (but, unfortunately, RETURN to a label is not allowed inside of a subroutine with SUB or FUNCTION). Also, subroutines called using GOSUB are in the same scope as the caller. (Usually, normal function calls would be used. But sometimes, GOSUB is helpful.)

GOTO is sometimes the best and clearest way to make something, although usually it will not be needed. Ideally, other flow controls such as FOR, WHILE, etc would be defined in terms of GOTO by use of hygienic macros, rather than being built-in features. You can then define your own flow controls too if you use some pattern a lot that giving that pattern its own name and macro is useful.

Direct dealing with the stack can be used to do some stuff normally done only in assembly language (although GNU C has some stuff, such as reading the stack pointer). But assembly language is specific to the target computer, and many libraries are not designed to be used with assembly language programs. However, some direct dealing with the stack might also depend on the target computer. Macros producing inline assembly code may be used if needed.

I do not know which other programming languages do such thing.

The concept of implementing high level control flow on top of a lower level control flow primitive via macro expansion is how most LISPs work. But instead of GOTO it's something more structured, like a match statement and recursion. It's also how compiler tend to lower for/while loops into IR before hitting the assembler. And it sucks to implement naively, since you need additional passes in your macro expansion/compiler to fold loop bodies and get decent vectorization without requiring a user to write it manually.

I don't really see the usefulness of GOSUB over normal function calls. Same with goto exposed to a user, it's not any more useful than typical control flow and is a giant footgun both in terms of logical errors made by a programmer and performance impact since arbitrary control flow tanks the ability of a compiler to reason about the program.

Still not sure what you need to do with the stack in assembly that can't be implemented in higher level constructs. There's good reason you can't mess with it too much even in low level languages, you can't always reason about stack frames in a given program or when integrating with programs compiled in other languages.

Well, I like "footgun" since it enables more possibilities. I don't like it to try to stop the programmer from writing a program.

But for the ability of a compiler to reason about the program, can't it first be converted to basic blocks anyways? Then it is irrelevant if you use goto or not, since the result will be same either way.

Footguns don't enable more possibilities other than the possibility to shoot yourself in the foot. Arbitrary control flow is pretty much the textbook example of that. It doesn't allow more expression or make it easier to express the same concepts, it makes it more difficult for the author, future readers, and tooling to understand what is being expressed in the first place. Not to say it isn't necessary for the implementation of languages on the hardware, just that the additional complexity of such a construct is better served by being generated from higher level syntax through automation like compilers or virtual machines.

As for the question of conversion to basic blocks, the question to me is at what point does your macro expansion turn into a compiler plugin or extension? I think the idea of hooking into the compiler at different points in the pipeline and expanding it on a per-project basis is really cool, and it's an old idea that's present in many LISPs. Racket and Scheme have extremely expressive macro systems that allow you to basically write a compiler. It'd be really interesting to have a language where the compiler/virtual machine had great hooks for extensions written by the author of a program being compiled, so they could do crazy things at progressive levels of granularity and unsafety. I think MLIR has some support for that, but it's more a target like LLVM than a language in its own right.

I would like to be able to delineate strings with any character(s) of my choosing. The default could be single or double or triple quotes, but don't force me. I am sick of escaping characters in literals, I'd rather define a delineator that I know isn't in the string. Same for templating.

   let name = "Jane";
   let str = s{}:John said "Don't do that, {name}!":s
I would also like a JSON-like shorthand for declaring nested structures, without having to define each individual component separately. Eg:

   struct Person {
      user: {string name, int age},
      address: {string street, ...}
   }
   let p = new Person(<json>);
   print(p.user.age);
Convenience with arrays (python slicing is nice), maps, and primitive types pay huge dividends.

   arr.first();
   arr.last();
   arr[5,-5];  //5th to 5th to last
Built-in arbitrary length/precision numbers would be neat.

Besides that, tons and tons of useful libraries: IO, Date, JSON, DB Bindings, etc.

As some already mentioned below, in my opinion it would depend on the purpose of the language. In the case of the general language, these are some of my personal favourites:

- expressive type system

- easy & intuitive to start with, but also powerful if needed (low floor, high ceiling)

- has a relatively small set of core concepts which are then composed together to build things further

- rich set of libraries (although pretty obvious and not really a feature)

I think our search for the "grail" general language will continue, probably forever. Languages such as Haskell, Rust & Go (and probably a lot of others I haven't heard of) are breaking the boundaries and introducing new concepts which then slowly spill over to more "mainstream" languages (e.g. the advent of functional programming constructs we are witnessing in Java, JS etc.).

Other than that, I also believe that more and more DSLs will start appearing, which is basically languages specialised for a specific purpose - which will make them extra convenient in their domain but not usable in the general case. Also, I expect the tooling (IDEs, build systems) to become more tightly integrated with the language, especially for the DSLs - creating the end product which at the end will be usable not only by the programming experts.

I have actually, together with my brother, been working on such an idea for the past year. We made a DSL for the domain of web apps - it is basically a declarative language that compiles to the stack of choice implemented in the general language (e.g. React/Node). This makes it possible to express some web-app specific concepts with much less code, but is also constrained to that specific domain.

If anybody wants to take a look it is here: https://github.com/wasp-lang/wasp

Would be interesting to see the results of your research and what majority thinks are the ideal language features.