Naming: linters deal with much more than formatting, and users tend to create linters even for languages which enforce canonical formatting.
Linters check for the patterns which are allowed by the compiler, but disallowed by a particular project/team/org. Example: disallow functions longer than four statements, disallow repeated strings that could be replaced by a constant, etc.
How are you side stepping an NP-hard issue like the halting problem?
Datalog is terminating. Programming languages don't have to allow infinite loops and recursion. There are plenty of ways to ensure recursion is terminating, like structural recursion or only allowing recursing finite data structures.
I guess you wouldn't be able to use it to build a machine that is supposed to display Pi or Fibinocci?
You can terminate that program when the user turns the engine off.
Or, you can say the program should consist of "read the stored state, re-configure the fuel injectors, fire a spark plug if one is supposed to fire now, update the stored state, and terminate". You could say that it should be written that way. But when you do, you've got a bunch of cynical old embedded software people saying, "Explain to me how that is better in any way? Does it make the program easier to write? No, it doesn't. Does it make it less error prone? More reliable? No and no. Does it make it use fewer resources? Also no. So why in the world do we want to write it that way?"
Go does exactly this (`go.mod`), and it turns out it still needs a package manager, just simpler. One of the reasons: diamond dependency problem.
This can be completely transparent to the user. There's no need to have a separate program do dependency resolution when dependencies are referenced in source code. Instead we have the complete waste of life that is package manifests and shared libraries.
To be clear the lack of “shared” dependencies does not necessitate code duplication. By shared dependencies I’m talking about languages (Haskell for example) where only one version of a library can be used, which results in the diamond dependency problem (predictably so!).
Let’s say file A references file B and C, and files B and C both reference D. D doesn’t need to be duplicated.
If all includes are qualified/namespaced there is no diamond dependency problem, and the compiler/runtime can reuse the same code for multiple references to the same file.
Yes, if an ecosystem does not attempt to ensure that D is at a single version, which is both B-compatible and C-compatible, it moves a mountain of complexity onto A.
It might not be apparent for a small program that only uses standard library and never experiences diamond dependencies at all. But the complexity here is that
1. A might get a D1 object/structure/result from B, and later
2. A might get a D2 object/structure/result from C, and then
3. Some code may be needed to ensure D1 compatibility with D2, if they interact. This problem is better to be resolved in an ecosystem than in A.
> - No higher-order functions.
I'm not creative enough to understand how you can do anything useful without these 2. Can you clarify?
No modules / namespaced includes means that to include other code, you would write something like `include as Foo './other-source-code.file'` or `include as Bar 'github.com/foo/bar/some/source.file@commit`. All the names in the included file (functions, names of data, etc.) would be referenced by prefixing the namespace, like `Foo.baz` or `Bar.baz`. Combine that with the ability to include code via URL, and there would be need for a package manager or separate package manifest. Wherever you want to use the new version/code you simply update the include statement at the top of the file.
- No linters. Code is auto-formatted.
- No modules. Includes can be namespaced/aliased.
- No package manager. Include by Git URL with tags.
- No type annotations. Automatic type-checking.
- No user-defined types. A few good types is better.
- No null. Set membership with maps is better.
- No exceptions.
- No loops. List and map comprehensions.
- No general recusion. Only tractable transitive closures (ala Datalog).
- No higher-order functions.
- Terminating. All programs terminate. Turing completeness is not desirable for most domains, but predictable semantics is.
- Safe. No undefined behavior. No crashes.
- Persistent, the runtime is also the database. The language is the database.
I'm working on a language with exactly this set of features. Email me if you're interested.