146 lines
7 KiB
Org Mode
146 lines
7 KiB
Org Mode
:PROPERTIES:
|
|
:ID: 31da574a-3a97-41e7-9513-764b55830ff1
|
|
:END:
|
|
#+Title: Programming Langage Ideas
|
|
#+Author: Yann Esposito
|
|
#+Date: [2023-08-05]
|
|
|
|
- tags :: [[id:bec11f07-ffed-487b-9059-bdf6696548ab][programming]]
|
|
- source ::
|
|
* Ideas
|
|
|
|
This is about a few nice ideas I had about what would be *my* ultimate programming language.
|
|
I am not sure they all make perfect sense. But we'll see.
|
|
|
|
First, most of them are kind of inspired by practice, idea about LISP. I think
|
|
most of these ideas aren't new at all. But their combination might be useful.
|
|
|
|
* Syntax Agnostic
|
|
|
|
The programming world uses text editors and not AST editor.
|
|
That's a fact and anyway, I don't want a programming language that would force
|
|
tooling on its users. Typically this is what made DrRacket not enjoyable.
|
|
I like my vim or emacs system. I don't want to use a strange IDE.
|
|
|
|
Also, people are generally used to a specific syntax. And let's be clear.
|
|
This TOTALLY SUCKS. Yep, this adds an obfuscation layer to the semantic.
|
|
Here, I am in the camp of LISPers that makes it a lost easier to internalize
|
|
the AST of your program.
|
|
But, even LISPs are not perfectly syntax agnostic.
|
|
|
|
Here is my proposed solution:
|
|
|
|
1. Have an internal AST representation.
|
|
2. From this representation ability to generate Text in different syntaxes,
|
|
mainly LISP or C/C++/C#/Python/Java/Javascript one.
|
|
Perhaps even Haskell/F#/OCaml-like.
|
|
Maybe for masochists a Bash/Perl one :) etc…
|
|
3. Have a builder that take the last modified date and sync every
|
|
representations. If you change the LISP file, it will update the internal
|
|
AST and the C-like.
|
|
If you change the C-like, update the LISP and internal AST.
|
|
If you directly modify the AST, then update all declared representations.
|
|
Mainly this jobs should be run a bit like a background make.
|
|
|
|
What should this solve:
|
|
|
|
1. If you join a new project, you can expose multiple syntaxes. So you can read
|
|
the code via Github for example by looking at your preferred syntax.
|
|
2. If you want to be 1337 dev, you can code a direct AST editor and this will
|
|
still make the change visible as Text for other editors.
|
|
=git diff= might kind of suck, but I think with minimal tooling this makes this acceptable.
|
|
3. Having a way to be agnostic about the syntax to prevent people saying: I
|
|
couldn't use that language due to its syntax. Which is really too bad.
|
|
It takes some time to get use to a new syntax, but once you've done the
|
|
effort to learn new programming languages a few times, it becomes a habit to
|
|
switch between different kind of syntaxes and you start to appreciate a
|
|
language for its semantic and put syntax concern in their right place, behind
|
|
the semantic of a language.
|
|
|
|
* Compile-Time Meta-constraints
|
|
|
|
Add a "mods" mechanism (a bit like in games like Factorio if you like)
|
|
that add "features" to your specific project (or even sub-part of your projects).
|
|
|
|
Typically I want to be able to express either for the whole project or specific
|
|
parts of the project:
|
|
+ every new namespace must be tested.
|
|
+ every new function must be unit-tested
|
|
+ Every namespace must have docstring
|
|
+ Every function must have a docstring
|
|
+ The project must have a sync'ed documentation
|
|
+ Force generative testing on pure functions
|
|
+ Every variable must have declared types for a specific type-system.
|
|
+ This sub-part of the project must be checked via a specific type-system
|
|
(hindly-milner, dependent typing with a specific base, linear typing, etc…)
|
|
|
|
More importantly, the important part is that this must be explicit.
|
|
One function wouldn't add a unit-test. No problem, but you MUST explicitly
|
|
say so.
|
|
|
|
How could we do this. Mainly by creating "Macros", mainly ability to add code
|
|
that will be run on your code at compile time to check that your code obey some
|
|
specific rules.
|
|
This would make a lot clearer that some code will be run at compile time.
|
|
This will also make possible to add different type-system depending on what your
|
|
project is focused on. For example, you can build specific type system to control
|
|
the complexity of a function. But for that, you will need a mechanism that will
|
|
take the AST and analyze it. And for that to work, you will need a system that
|
|
will "only" add metas (so AST-level annotation) for a few core functions in the language.
|
|
And if you use an "external", the module should ask you to manually annotate
|
|
these unknown functions.
|
|
|
|
But mainly we want a mechanism like the clojure metas, that could be used to run
|
|
compile-time checks.
|
|
|
|
Note, it would still be helpful to keep this metas at runtime depending on your need.
|
|
But I am not sure how to correctly choose between compile-time only vs compile+run-time.
|
|
Because if we allow run-time AST evaluation then, this will make the language a
|
|
lot more powerful at the risk of making it a lot more difficult to check at
|
|
compile-time and reduce a lot of compile-time advantages.
|
|
|
|
* Service-compatible in the Language
|
|
|
|
The Service-Pattern is probably universal but there are many choices here.
|
|
Perhaps, the best place to put this would be to put this structure in the mods
|
|
and not directly in the language.
|
|
But it would be very nice to have a well-designed service-dependency system.
|
|
|
|
More precisely, we want to be able to write programs with:
|
|
|
|
- Run ~main~ with this LogService, and DBService and, intialized with this ConfigService
|
|
|
|
* Have great "defaults"
|
|
|
|
I feel that if you take the time to look at Programming language evolution and
|
|
history, what really makes the big differences between two programming language
|
|
(at least for me) is their choice of "default".
|
|
|
|
Building the greatest programming language is about providing the ability to
|
|
choose, but more importantly, providing the ability to give the best default
|
|
behaviour so using the "non-default" more difficult to use and thus be somehow
|
|
punished by complexity.
|
|
|
|
A good example is about old PHP SQL libs vs modern Haskell SQL libs.
|
|
Mainly the main thing that changed is that before it was insecure by default,
|
|
and the security concern was put as a burden to the developer to take care of.
|
|
Of course, due to time-pressure and/or lazyness and/or incompetence, it was
|
|
pretty natural to see a big number of security bug flourish everywhere.
|
|
While if you use a modern lib, now, it is secure by default.
|
|
|
|
So:
|
|
|
|
- immutable data structures by default (this has become a norm for great new languages,
|
|
Haskell, Clojure, Rust, etc…)
|
|
- statically checked by default (statically checked is more generic than typed
|
|
by default) I think, it is important
|
|
- documentable by default (Clojure already provides internal docstring and this
|
|
is important, I think we should forbid text-only comments and replace them by
|
|
contextual-aware comments)
|
|
- debuggable, traceable by default (this one is probably a bit more difficult to
|
|
be precise about. But you want your language to help his developer in not only
|
|
detecting an error or a problem in its code, but give hints about how to help
|
|
solve them. Elm did an incredible job at this).
|
|
Mainly, ~log~ should be treated seriously and as 1st class in the language and
|
|
also, not text-only but using structured logs that could be put in a DB for
|
|
search in the future.
|