[HN Gopher] Some programming language ideas
       ___________________________________________________________________
        
       Some programming language ideas
        
       Author : todsacerdoti
       Score  : 231 points
       Date   : 2025-01-08 18:58 UTC (1 days ago)
        
 (HTM) web link (jerf.org)
 (TXT) w3m dump (jerf.org)
        
       | a1o wrote:
       | Is it just me or whatever "Capabilities" is, is not explained at
       | all?
        
         | jerf wrote:
         | It is not. I didn't want to give a half explanation, but it is
         | another case of the increasing difficulty in coming up with
         | good Google searches anymore.
         | 
         | https://erights.org/elib/capability/ode/ode-capabilities.htm...
         | is a good start.
         | 
         | But you use capabilities all the time... operating system users
         | work that way. As a user, you can't "just" execute some binary
         | somewhere and thereby get access to parts of the system your
         | user doesn't have rights to. (Forget setuid for a second, which
         | is intended precisely to get around this, and let's just look
         | at the underlying primitive.)
         | 
         | Capabilities in programming languages take the granularity
         | further down. You might call some image manipulation code in a
         | way that it doesn't have the capability to manipulate the file
         | system in general, for example, or call a function to change a
         | user's login name with capabilities that _only_ allow changing
         | that user, even if another user ID somehow gets in there.
         | 
         | It would be a fairly comprehensive answer to the software
         | dependency issues that continue to bubble up; it would matter
         | less if a bad actor took over "leftpad" if leftpad was actively
         | constrained by the language to _only_ be able to manipulate
         | strings, so the worst an actor could do is make it manipulate
         | strings the wrong way, rather than start running arbitrary
         | code. Or put another way, if the result of the bad actor taking
         | the package wasn 't that people got hacked but users started
         | getting                   compile error in file.X:28: library
         | "leftpad" tried to open a file without file system capabilities
         | compile error in file.X:30: library "leftpad" tried to open a
         | socket without network capabilities
         | 
         | which would immediately raise eyebrows.
         | 
         | It's not a new idea, in that E already tried it, and bits and
         | pieces of it are _everywhere_ ( "microkernels" is another place
         | where you'll see this idea, but at the OS level and implemented
         | in languages that have no native concept of the capabilities),
         | but for the most part our programming languages do not reflect
         | this.
        
           | NeutralForest wrote:
           | I think the Austral language tries to do some capability
           | based things: https://austral-lang.org/.
        
             | a1o wrote:
             | Thanks for this link!
             | 
             | It has this other link that explains more on it :
             | https://en.m.wikipedia.org/wiki/Capability-based_security
             | 
             | I think I get it now. I honestly never had heard about this
             | before and trying to Google search from the original post I
             | was coming up empty.
        
           | sparkie wrote:
           | > But you use capabilities all the time... operating system
           | users work that way.
           | 
           | Most operating systems don't have proper capabilities - they
           | use things like ACLS, RBAC, MAC, etc for permissions.
           | 
           | The golden rule of capabilities is that you should not
           | separate designation from authority. The capability itself
           | represents the authority to access something, and designates
           | what is being accessed.
        
         | RainyDayTmrw wrote:
         | For the equivalent in operating systems land, look at the
         | respective manual pages for Linux capabilities[1] or OpenBSD
         | pledge[2] and unveil[3]. The general idea is that there are
         | some operations that might be dangerous, and maybe we don't
         | want our program to have unrestricted access to them. Instead,
         | we opt-in to the subset that we know we need, and don't have
         | access to the rest.
         | 
         | There's some interest in the same thing, but at the programming
         | language level. I'm only aware of it being implemented
         | academically.
         | 
         | [1]: https://man7.org/linux/man-pages/man7/capabilities.7.html
         | [2]: https://man.openbsd.org/pledge.2 [3]:
         | https://man.openbsd.org/unveil.2
        
           | mike_hearn wrote:
           | It's implemented in Java! .NET tried it too, UNIX file
           | descriptors are capabilities, Mach ports are capabilities.
           | Capabilities are widely used far outside of academia and have
           | been for a long time.
           | 
           | What people often mean when they say this is a so-called pure
           | capability system, where there are no ambient permissions at
           | all. Such systems have terrible usability and indeed have
           | never been made to work anywhere, not even in academia as far
           | as I know.
        
           | gpderetta wrote:
           | I don't think that Linux capabilities have much to do with
           | the capabilities that the OP intends.
           | 
           | In a capabilities system, a program has permission to act on
           | any object if it has a reference (aka a capability) to the
           | object, there is no other access control. A program acquires
           | a capability either by receiving it from is parent (or caller
           | in the case of a function) or some other way like message
           | passing. There is no other source of capabilities and they
           | are unforgeable.
           | 
           | Unix file descriptors act in many ways as capabilities: they
           | are inherited by processes from their parents and can be
           | passed around via Unix sockets, and grant to the FD holder
           | the same permissions to the referenced object as the creator
           | of the file descriptor.
           | 
           | Of course as Unix has other ways from creating file
           | descriptors other than inheritance and message passing is not
           | truly a capabilities system.
        
         | marcosdumay wrote:
         | > This is not a new idea, so I won't go deeply into what it is
         | 
         | So, no, the author claims it too.
         | 
         | Capabilities are a way to do access control where the client
         | holds the key to access something, instead of the server holds
         | a list of what is allowed based on the clients identities.
         | 
         | But when people use that word, they are usually talking about
         | fine-grained access control. On a language level, that would
         | mean not granting access for example for a library to do
         | network connections, even though your program as a whole has
         | that kind of access.
        
           | chriswarbo wrote:
           | Kind of. At a more fundamental level, it's applying the idea
           | that "invalid states should be unrepresentable" (e.g.
           | https://hugotunius.se/2020/05/16/making-invalid-state-
           | unrepr... ) but to our code itself.
           | 
           | For example, consider a simple function to copy files. We
           | _could_ implement it like this:                   def
           | copy(fs: Filesystem, in: Path, out: Path) {           inH:
           | HandleRead = fs.openRead(in);           outH: HandleWrite =
           | fs.openWrite("/tmp/TEST_OUTPUT");           finished: Boolean
           | = false;           while (!finished) {             match
           | (inH.read()) {               case None: finished = true;
           | case Some(data) = outH.write(data);             }           }
           | inH.close();           outH.close();         }
           | 
           | However, there are many ways that things could go awry when
           | writing code like this; e.g. it will write to the wrong file,
           | since I forgot to put the real `out` value back after testing
           | (oops!). Such problems are only possible because we've given
           | this function the capability to call `fs.open` (in many
           | languages the situation's even worse, since that capability
           | is "ambient": available everywhere, without having to be
           | passed in like `fs` above). There are also other
           | capabilities/permissions/authorities implicit in this code,
           | since any call to `fs.open` has to have the right permissions
           | to read/write those files.
           | 
           | In contrast, consider this alternative implementation:
           | def copy(inH: HandleRead, outH: HandleWrite) {
           | finished: Boolean = false;           while (!finished) {
           | match (inH.read()) {               case None: finished =
           | true;               case Some(data) = outH.write(data);
           | }           }           inH.close();           outH.close();
           | }
           | 
           | This version _can 't_ use the wrong files, since it doesn't
           | have any access to the filesystem: there's literally nothing
           | we could write here that would mean "open a file"; it's
           | unrepresentable. This code also can't mix up the
           | input/output, since only `inH` has a `.read()` method and
           | only `outH` has a `.write()` method. The `fs.open` calls will
           | still need to be made somewhere, but there's no reason to
           | give our `copy` function that capability.
           | 
           | In fact, we can see the same thing on the CLI:
           | 
           | - The first version is like `cp oldPath newPath`. Here, the
           | `cp` command needs access to the filesystem, it needs
           | permission to open files, and we have to trust that it won't
           | open the wrong files.
           | 
           | - The second version is like `cat < oldPath > newPath`. The
           | `cat` command doesn't need any filesystem access or
           | permissions, it just dumps data from stdin to stdout; and
           | there's no way it can get them mixed up.
           | 
           | The fundamental idea is that trying to choose whether an
           | action should be allowed or not (e.g. based on permissions)
           | is too late. It's better if those who shouldn't be allowed to
           | do an action, aren't even able to express it at all.
           | 
           | You're right that this can often involve "keys", but that's
           | quite artificial: it's like adding extra arguments to each
           | function, and limiting which code is scoped to see the values
           | that need to be passed as those arguments (e.g.
           | `fs.openRead(inPath, keyThatAllowsAccess)`), when we could
           | have instead scoped our code to limit access to the functions
           | themselves (though for HTTP APIs, everything is a URL; so
           | "unguessable function endpoint URL" is essentially the same
           | as "URL with secret key in it")
        
         | dboreham wrote:
         | It's a fancy word for "access things through a handle".
        
           | gpderetta wrote:
           | The key property being that everything can only be accessed
           | via handles, including, recursively, other handles (i.e. to
           | get an handle to an object you need first to already have an
           | handle to the handle-giver for that object).
        
         | sparkie wrote:
         | A capability is basically a reference which both designates
         | some resource to be accessed _and_ provides the authority to
         | access it. The authority is not held somewhere else like an
         | Access Control List - the reference _is_ the authority.
         | Capabilities must be _unforgeable_ - they 're obtained by
         | delegation.
         | 
         | ---
         | 
         | To give an example of where this has been used in a programming
         | language, Kernel[1] uses a capability model for mutating
         | environments. Every function (or operative) has an environment
         | which holds all of its local variables, the environment is
         | encapsulated and internally holds a reference to the parent
         | environment (the surrounding scope). The rule is that we can
         | only mutate the local variables of an environment to which we
         | have a direct reference, but we cannot mutate variables in the
         | parents. In order to mutate the variables in the parent, we
         | must have a _direct reference_ to the parent, but there is no
         | mechanism in the language to extract the parent reference from
         | the environment it is encapsulated in.
         | 
         | For example, consider the following trivial bit of code: We
         | define some variable `x` with initial value "foo", we then
         | mutate it to have the value "bar", then look up `x`.
         | ($define! x "foo")         ($set! (get-current-environment) x
         | "bar")          x
         | 
         | As expected, this returns "bar". We have a direct reference to
         | the local environment via `(get-current-environment)`.
         | 
         | Technically we could've just written `($define! x "bar")`,
         | where the current environment is assumed, but I used `$set!`
         | because we need it for the next example.
         | 
         | When we introduce a new scope, the story is different.
         | ($define! x "foo")         ($define! foo           ($lambda ()
         | ($set! (get-current-environment) x "bar")))         (foo)
         | x
         | 
         | Here we create a function foo, which has its own local
         | environment, with the top-level environment as its parent. We
         | can _read_ `x` from inside this environment, but we can 't
         | mutate it. In fact, this code inserts a new variable `x` into
         | the child environment which shadows the existing one within the
         | scope of the function, but after `foo` has returned, this
         | environment is lost, so the result of the computation is "foo".
         | There is no way for the body of this lambda to mutate the top-
         | level environment here because it doesn't have a direct
         | reference to it.
         | 
         | So far basically the same static scoping rules you are used to,
         | but environments in Kernel are first-class, so we _can_ get a
         | reference to the top-level environment which grants the child
         | environment the authority to mutate the top level environment.
         | ($define! x "foo")         ($define! env (get-current-
         | environment))         ($define! foo           ($lambda ()
         | ($set! env x "bar")))         (foo)         x
         | 
         | And the result of this computation is "bar".
         | 
         | However, by binding `env` in the top-level environment, all
         | child scopes can now have the ability to mutate the top-level.
         | 
         | To avoid polluting the environment in such way, the better way
         | to write this is with an _operative_ (as opposed to $lambda),
         | which implicitly receives the caller 's environment as an
         | argument, which it binds to a variable in its local
         | environment.                   ($define! x "foo")
         | ($define! foo           (wrap ($vau () caller-env
         | ($set! caller-env x "bar"))))         (foo)         x
         | 
         | Now `foo` specifically can mutate it's caller's _local_
         | environment, but it can 't mutate the variables of the caller
         | of the caller, and we have not exposed this authority to all
         | children of the top-level.
         | 
         | ---
         | 
         | This is only a trivial example, but we can do much more clever
         | things with environments in Kernel. We can construct new
         | environments at runtime, and they can have multiple parents,
         | ultimately forming a DAG, where environment lookup is a Depth-
         | First-Search, but the references to the parent environments are
         | encapsulated and cannot be accessed, so we cannot mutate parent
         | scopes without a direct reference - we can only mutate the root
         | node of the DAG for an environment to which we have a direct
         | reference. The direct reference is a _capability_ - it 's both
         | the means and the authority to mutate.
         | 
         | ---
         | 
         | We can use these first-class environments in conjunction with
         | things like `$remote-eval`, which evaluates some piece of code
         | in an environment provided by the user, which may contain only
         | the bindings they specify, and does not capture anything from
         | the surrounding scope.                 ($define! calculator-
         | environment         ($bindings->environment (+ +) (- -) (* *)
         | (/ /)))            ($remote-eval (+ 1 2) calculator-
         | environment)
         | 
         | However, if we try to use some feature like IO, to write an
         | output to the console.                 ($remote-eval (write (+
         | 1 2)) calculator-environment)
         | 
         | We get an error, `write` is unbound - even though `write` is
         | available in the scope in which we performed this evaluation.
         | We could catch this error with a guarded continuation so the
         | program does not crash.
         | 
         | This combination of features basically let you create "mini
         | sandboxes", or custom DSLs, with more limited capabilities than
         | the context in which they're evaluated. Most languages only let
         | you _add new capabilities_ to the static environment, by
         | defining new functions and types - but how many languages let
         | you _subtract_ capabilities, so that fewer features are
         | available in a given context? Most languages do this purely at
         | compile time via a module /import system, or with static access
         | modifiers like `public` and `private`. Kernel lets you do this
         | at runtime.
         | 
         | ---
         | 
         | One thing missing from this example, which is required for
         | _true capabilities_ , is the ability to revoke the authority.
         | The only way we could revoke the capability of a function to
         | mutate an environment is to suspend the program.
         | 
         | Proper capabilities allow revocation _at any time_. If the
         | creator of a capability revokes the authority, this should
         | propagate to all duplicated, delegated, or derived capabilities
         | with immediate effect. The capabilities that were held become
         | "zombies", which no longer provide the means nor the authority
         | - and this is why it is essential that we don't separate
         | designation from authority, and why these should both be
         | encapsulated in the capability.
         | 
         | This clearly makes it difficult to provide proper capabilities
         | in programming languages, because we have to handle every
         | possible error where we attempt to access a zombie capability.
         | The use of such capabilities should be limited to where they
         | really matter such as access to operating system resources,
         | cryptographic keys, etc. where it's reasonable to implement
         | robust error handling code. We don't want capabilities for
         | every programming language feature because we would need to
         | insert error checks on every expression to handle the potential
         | zombie. Attempting to check if a capability is live before
         | using it is no solution anyway, because you would have race
         | conditions, so the correct approach to using them is to just
         | try and catch the error if it occurs.
         | 
         | Another take-away from this is that if capabilities are
         | provided in a language via a type system, _it must be a dynamic
         | type system_. You cannot grant authority in a static type
         | system at compile time if the capability may have already been
         | revoked by the time the program is run. Capabilities are
         | inherently dynamic by nature because they can be revoked _at
         | any time_. This doesn 't mean you can't use capabilities in
         | conjunction with a static type system - only that the static
         | type system can't really represent capabilities.
         | 
         | You can find out a lot more about them on the erights page that
         | others have linked, and I would recommend looking into seL4 if
         | you're interested in how they're applied to operating systems.
         | 
         | ---
         | 
         | [1]:http://web.cs.wpi.edu/%7Ejshutt/kernel.html
        
       | wslh wrote:
       | My 4 cents:
       | 
       | - I like the idea of a multiparadigm programming language (many
       | exists) but where you can write part of the code in a different
       | language, not trying to embed everything in the same syntax. I
       | think in this way you can write code and express your ideas
       | differently.
       | 
       | - A [social] programming language where some variables and
       | workflows are shared between users [1][2].
       | 
       | - A superreflective programming language inspired by Python,
       | Ruby, and others where you can override practically everything to
       | behave different. For example, in Python you can override a
       | function call for an object but not for the base system,
       | globals() dict cannot be overriden. See [3]. In this way you save
       | a lot of time writing a parser and the language basic logic.
       | 
       | - A declarative language to stop reinventing the wheel: "I need a
       | website with a secure login/logout/forgot_your_password_etc,
       | choose a random() template". It doesn't need to be in natural
       | language though.
       | 
       | [1] https://blog.databigbang.com/ideas-egont-a-web-
       | orchestration...
       | 
       | [2] https://blog.databigbang.com/egont-part-ii/
       | 
       | [3] https://www.moserware.com/2008/06/ometa-who-what-when-
       | where-...
        
         | mike_hearn wrote:
         | Egont sounds a bit like SQL, no? A social way to share data and
         | work with it ... a shared RDBMS where everyone has a user
         | account and can create tables/share them with other users,
         | built in security, etc. Splat a GUI on top and you have
         | something similar.
         | 
         | Modern web frameworks are getting pretty declarative. If you
         | want a basic web app with a log in/out page that's not hard to
         | do. I'm more familiar with Micronaut than Spring but you'd just
         | add:                   micronaut.security.authentication=cookie
         | 
         | and the relevant dependencies. Now you write a class that
         | checks the username/password, or use LDAP, or configure OAuth
         | and the /login URL takes a POST of username/password. Write a
         | bit of HTML that looks good for your website and you're done.
         | 
         | https://micronaut-projects.github.io/micronaut-security/late...
        
           | wslh wrote:
           | > Egont sounds a bit like SQL, no? A social way to share data
           | and work with it ... a shared RDBMS where everyone has a user
           | account and can create tables/share them with other users,
           | built in security, etc. Splat a GUI on top and you have
           | something similar.
           | 
           | Yes, SQL or a global spreadsheet. I would say that it is like
           | SQL plus a DAG or, we can imagine an aggregation of SQLs. The
           | interesting thing is that parts of the global system are only
           | recalculated if there is a change, like in a spreadsheet.
           | 
           | > a shared RDBMS where everyone has a user account and can
           | create tables/share them with other users, built in security,
           | etc. Splat a GUI on top and you have something similar.
           | 
           | We need a little bit more but not much more: security by
           | namespaces and/or rows so the same database is shared but you
           | can restrict who change what: your "rows" are yours. I think
           | something like OrbitDB but with namespaces will be cool.
           | 
           | > Modern web frameworks are getting pretty declarative.
           | 
           | Yes but my proposal was at a higher level. I don't want to
           | know what a cookie is when I just want to create a website. I
           | am not saying that you can create complex components with
           | this idea but you can create common use cases.
        
       | nickpsecurity wrote:
       | For relational, look into term-rewriting systems which just keep
       | transforming specified relationships into other things. Maude's
       | rewriting logic and engine could probably be used for relational
       | programming. It's fast, too.
       | 
       | https://maude.cs.illinois.edu/wiki/The_Maude_System
        
       | AnimalMuppet wrote:
       | For "value database", it seems to me that the trick is, you can't
       | just ship the executable. You have to ship the executable plus
       | the stored values, together, as your installation image. Then
       | what you run in production is what you tested in staging, which
       | is what you debugged on your development system.
       | 
       | I mean, there still may be other things that make this a Bad
       | Idea(TM). But I think this could get around the specific issue
       | mentioned in the article.
        
         | marcosdumay wrote:
         | Basically, it's the Windows registry.
         | 
         | If it's about well-contained applications in a well designed
         | (and user-centric) OS with a proper concept of "application"
         | and "installation", with a usable enough mechanism, I don't see
         | anything that would make it bad.
         | 
         | On Windows it's a disaster. To the point that dumping random
         | text files around in Linux works better.
        
       | jinwoo68 wrote:
       | It's not very convincing to me when the article talks about truly
       | relational language but fails to mention Prolog and anything that
       | we learned from it.
        
         | jerf wrote:
         | Logic languages are definitely not what I'd expect a
         | relational-first language to look like.
         | 
         | What we learned from Prolog is mostly that starting from an
         | exponentially-complex primitive and then trying to beat it into
         | submission doesn't work at scale. Relational DBs don't have
         | that problem. They do go n-squared and n-cubed and so forth
         | easily, but there are lots of solutions to that as well.
        
           | jinwoo68 wrote:
           | I'm not sure what you mean with "an exponentially-complex
           | primitive". In my opinion, Prolog lets you start with simple
           | relations (n-squared, using your terms) and then enables you
           | to build more complex relations using them.
        
       | 082349872349872 wrote:
       | for "Semi-Dynamic Language" it might be worth looking into
       | rpython: interpreters written in rpython have two phases, in the
       | first phase one has full python semantics, but in the second
       | phase everything is assumed to be less dynamic, more restricted
       | (the _r_ of rpython?) so the residual interpreter is then
       | transpiled to C sources, which, although compiled, can also make
       | use of the built-in GC and JIT.
        
         | MillironX wrote:
         | I immediately thought of Julia as a semi-dynamic language.
         | Julia is a dynamic language, but (as I understand it) the first
         | time a function is called with a specific type signature, that
         | specific method is JIT compiled as static LLVM.
        
           | Jtsummers wrote:
           | Which is then used for future dispatches on that same
           | signature and gives it very good performance. Julia is
           | dynamic, and definitely beats the 10x slower than C barrier
           | jerf mentioned.
           | 
           | For what I was using it for at the time (~3 years ago when I
           | used it seriously) it offered performance close to the
           | compiled orbital analysis code we had (in more conventional
           | languages, Fortran and C) but with the flexibility for
           | developing models of Python and other dynamic/interactive
           | languages. An excellent tradeoff: very small performance cost
           | for better interactivity and flexibility.
        
         | rirze wrote:
         | Sans the python compatiblity, rhai is pretty close to what
         | rpython is trying to be.
         | 
         | https://github.com/rhaiscript/rhai
        
           | gsf_emergency wrote:
           | >https://github.com/rhaiscript/rhai#for-those-who-actually-
           | wa...
           | 
           | ([ [?]D[?])[
        
         | gsf_emergency wrote:
         | Of all the non-esolangs (=exolangs?) APL+kith seem to be almost
         | designorismic/estuarine (formerly, Riverian) beasts..
         | 
         | In your informed opinion, how would it make sense to be
         | thrilled thinking about (not just a semi-dynamic APL (=S-DAPL?)
         | as above but) designing                 a APL-kith for
         | widespread adoption by BOTH (demoscenic)gamecoders & Analysts
         | (the academic variety)???
         | 
         | Specifically, which of the sneering checklist items[0] would be
         | killer to cross off?
         | 
         | [0] https://www.mcmillen.dev/language_checklist.html
         | 
         | (Note in particular that the very APL inspired Wolfram has
         | monopoly with physicists BUT does nothing for engineers
         | 
         | https://www.stephenwolfram.com/media/physics-whiz-goes-into-...
         | 
         | 1988
         | 
         | > _"But we tricked him, so to speak," says Nobelist Murray
         | Gell-Mann, who helped to bring Wolfram west. "We gave him a
         | Ph.D."_ )
        
       | continuational wrote:
       | Interesting points!
       | 
       | We're working on a language with some of these ideas:
       | 
       | https://www.firefly-lang.org/
       | 
       | Object capabilities, async calls as easy as sync calls, modular
       | monoliths, and (eventually) unified logging.
       | 
       | None of the relational language features though.
       | 
       | Feedback appreciated!
        
       | Animats wrote:
       | - Looser functions (badly chosen name)
       | 
       | Timeouts on calls are, as the OP mentions, a thing in Erlang.
       | Inter-process and inter-computer calls in QNX can optionally time
       | out, and this includes all system calls that can block. Real-time
       | programs use such features. Probably don't want it on more than
       | that. It's like having exceptions raised in things you thought
       | worked.
       | 
       | - Capabilities
       | 
       | They've been tried at the hardware level, and IBM used them in
       | the System/38, but they never caught on. They're not really
       | compatible with C's flat memory model, which is partly they fell
       | out of fashion. Capabilities mean having multiple types of
       | memory. Might come back if partially-shared multiprocessors make
       | a comeback.
       | 
       | - Production-Level Releases
       | 
       | That's kind of vague. Semantic versioning is a related concept.
       | It's more of a tooling thing than a language thing.
       | 
       | - Semi-Dynamic Language
       | 
       | I once proposed this for Python. The idea was that, at some
       | point, the program made a call that told the system "Done
       | initializing". After that point, you couldn't load more code, and
       | some other things that inhibit optimization would be prohibited.
       | At that point, the JIT compiler runs, once. No need for the
       | horrors inside PyPy which deal with cleanup when someone patches
       | one module from another.
       | 
       | Guido didn't like it.
       | 
       | - Value Database
       | 
       | The OP has a good criticism of why this is a bad idea. It's an
       | old idea, mostly from LISP land, where early systems saved the
       | whole LISP environment state. Source control? What's that?
       | 
       | - A Truly Relational Language
       | 
       | Well, in Python, almost everything is a key/value store. The
       | NoSQL people were going in that direction. Then people remembered
       | that you want atomic transactions to keep the database from
       | turning to junk, and mostly backed off from NoSQL where the data
       | matters long-term.
       | 
       | - A Language To Encourage Modular Monoliths
       | 
       | Hm. Needs further development. Yes, we still have trouble putting
       | parts together. There's been real progress. Nobody has to keep
       | rewriting Vol. I of Knuth algorithms in each new project any
       | more. But what's being proposed here?
       | 
       | - Modular Linting
       | 
       | That's mostly a hack for when the original language design was
       | botched. View this from the point of the maintenance programmer -
       | what guarantees apply to this code? What's been prevented from
       | happening? Rust has one linter, and you can add directives _in
       | the code_ which allow exceptions. This allows future maintenance
       | programmers to see what is being allowed.
        
         | mplanchard wrote:
         | > In general, while I can't control how people react to this
         | list, should this end up on, say, Hacker News, I'm looking more
         | for replies of the form "that's interesting and it makes me
         | think of this other interesting idea" and less "that's stupid
         | and could never work because X, Y, and Z so everyone stop
         | talking about new ideas" or "why hasn't jerf heard of this
         | other obscure language that tried that 30 years ago". (Because,
         | again, of course I don't know everything that has been tried.)
        
           | Animats wrote:
           | OK, a few things that languages ought to have:
           | 
           | - Everything except C now has standard strings, not just
           | arrays of characters. Almost all languages now have some
           | standard way to do key/value sets. What else ought to be
           | standard?
           | 
           | -- Arrays of more than one dimension would be helpful for
           | numerical work. Most languages descended from C lack this.
           | They only have arrays of arrays. Even Rust lacks it.
           | Proposals run into bikeshedding - some people want
           | rectangular slices out of arrays, which means carrying stride
           | info around.
           | 
           | -- Standard types for 2, 3 and 4-element vectors would help
           | in graphics work. There are too many different
           | implementations of those in most language and too much
           | conversion.
           | 
           | Things to think about:
           | 
           | - Rust's ownership restrictions are harsh. Can we keep the
           | safety and do more?
           | 
           | -- The back-reference problem needs to be solved somehow.
           | Back references can be done with Rc and Weak, but it's
           | clunky.
           | 
           | -- Can what Rust does with Rc, RefCell, and .borrow() be
           | checked at compile time? That allows eliminating the run-time
           | check, and provides assurance that the run-time check won't
           | fail. Something has to look at the entire call tree at
           | compile time, and sometimes it won't be possible to verify
           | this at compile time. But most of the time, it should be.
           | 
           | -- There's a scheme for ownership where there's one owning
           | reference and N using references. The idea is to verify at
           | compile time that the using references cannot outlive the
           | owning one. Then there's no need for reference counds.
           | 
           | -- Can this be extended to the multi-thread case? There have
           | been academic demos of static deadlock detection, but that
           | doesn't seem to have made it into production languages.
           | 
           | -- A common idiom involves things being owned by handles, but
           | also indexed for lookup by various keys. Dropping the handle
           | drops the object and removes it from the indices. Is that a
           | useful general purpose operation? It's one that gets botched
           | rather often.
           | 
           | -- Should compilers have SAT-solver level proof systems built
           | in?
           | 
           | -- Do programs really have to be in monospaced fonts? (Mesa
           | on the Alto used the Bravo word processor as its text editor.
           | Nobody does that any more.)
           | 
           | -- There's async, there are threads, and there are "green
           | threads", such as Go's "goroutines". Where's that going?
           | 
           | -- Can we have programs which run partly in a CPU and partly
           | in a GPU, compiled together with the appropriate consistency
           | checks, so the data structures and calls must match to
           | compile?
           | 
           | -- How about "big objects?" These are separately built
           | program components which have internal state and some
           | protection from their callers. Microsoft OLE did that, some
           | .dll files do that, and Intel used to have rings of
           | protection and call gates to help with that, hardware
           | features nobody used. But languages never directly supported
           | such objects.
           | 
           | So there are a few simple ideas to think about.
        
             | pmontra wrote:
             | > Do programs really have to be in monospaced fonts?
             | 
             | Of course not. I've been using a proportional font for at
             | least 10 years and I'm still in business working on code
             | bases shared with developers using monospaced fonts. Both
             | work, none disturb the other, proportional is easier to
             | read as any book can demonstrate. Alignment doesn't matter
             | much.
        
               | cmontella wrote:
               | How do you deal with writing code with multicursors when
               | you have to type the same thing multiple times? With
               | monospace I just ctrl+alt+down a couple times on aligned
               | text and then type. With proportional fonts I don't
               | suppose it's easy to align text exactly, so do you just
               | not use multicursors or is there a solution you came up
               | with that works?
        
               | kridsdale1 wrote:
               | I've been a professional programmer for 17 years and have
               | never used multicursors. I don't even fathom under what
               | conditions you'd want to. I use Find and Replace.
        
               | cozzyd wrote:
               | Yes and ctrl-V in vim would be super awkward.
        
             | tylerhou wrote:
             | > - Rust's ownership restrictions are harsh. Can we keep
             | the safety and do more?
             | 
             | https://www.languagesforsyste.ms/publication/fearless-
             | concur...
             | 
             | > -- Should compilers have SAT-solver level proof systems
             | built in?
             | 
             | They already do. The exhaustivity checker in Rust (and
             | functional languages) is equivalent in power to SAT.
        
             | uecker wrote:
             | C has arrays with more than one dimension. (but no direct
             | support for strides). Of course, it is just the same thing
             | as arrays of arrays.
        
               | sigsev_251 wrote:
               | Are C multidimensional arrays guaranteed to be contiguous
               | in memory? In practice they are, but can one iterate
               | through them just by incrementing a pointer which points
               | to the first element without UB?
        
               | krapp wrote:
               | Yes. All arrays in C are contiguous. Multidimensional
               | arrays are just arrays of arrays, and are therefore also
               | contiguous.
               | 
               | Source: https://stackoverflow.com/questions/36647286/are-
               | c-multidime...
        
               | sigsev_251 wrote:
               | Yes, but is one allowed to move a pointer inside it as
               | they see fit? On a one-dimensional array, one can iterate
               | through it starting with a pointer pointing to the first
               | element and ending with a pointer pointing one position
               | past the last element (which the user is not allowed to
               | dereference). For multidimensional arrays, the element
               | type is an array too (with a smaller rank than the
               | original one), so one could perform that type of
               | iteration with a pointer to an array. My question is
               | whether a pointer to the underlying scalar type can
               | freely move inside the multidimensional array without UB,
               | since it may have to actually leave the array it was
               | originally part of. If that's not allowed, how could one
               | build slices and other view types?
        
               | krapp wrote:
               | Yes, you can do that, it's fine as long as you stay
               | within the bounds of the indexes. Under the hood, it's a
               | single contiguous block of memory.
               | 
               | Although at least with 2d arrays I prefer to just use a
               | 1d array and index it with [x * width + y], because one
               | problem with multidimensional arrays in C is they need
               | multiple allocations/frees.
        
               | sigsev_251 wrote:
               | Why would you need multiple allocations?
               | 
               | edit: Isn't it just:                 float (*arr)[m][n] =
               | malloc(sizeof(*arr));
        
               | krapp wrote:
               | You have to allocate the size for m and n, because C
               | arrays decay bare pointers.
               | 
               | But it seems to work, which I didn't expect[0]. It also
               | seems like you have to de-reference it to use it which is
               | weird.
               | 
               | [0]https://onlinegdb.com/HwX-WTL5t
        
               | uecker wrote:
               | Double indirection arrays with multiple allocations are
               | 25 years obsolete (ok, there are some use cases) but
               | since C99 we prefer to do it like the parent.
               | 
               | In your code link you over allocate memory, sizeof *arr
               | is enough and you need to dereference like with
               | (*arr)[i][j]. You need to dereference it because it is a
               | pointer to an array, if you dereference you get an array.
               | You can also let the first dimensions decay then it looks
               | like:                 double (*arr)[m] = malloc(n *
               | sizeof *arr);       arr[i][j] = ...
               | 
               | but this is not as safe because the bound of the
               | outermost dimension is lost. (Edited)
        
             | zzo38computer wrote:
             | > Everything except C now has standard strings, not just
             | arrays of characters. Almost all languages now have some
             | standard way to do key/value sets. What else ought to be
             | standard?
             | 
             | I think that character strings should not be restricted to
             | (or generally expected to be) Unicode (although using
             | Unicode and other character sets will still be possible).
             | 
             | I also think that key/value lists should allow any or most
             | types as keys, including references to objects. (PostScript
             | allows any type to be used as keys except strings (they are
             | converted to names if you use them as keys) and nulls.)
             | 
             | I think that big integers (which can have a program-
             | specified limited length in programming languages with
             | typed variables) and arrays of fixed-length records (which
             | C already has; JavaScript has typed arrays which is a more
             | limited implementation of this) are also things that would
             | be helpful to include as standard.
             | 
             | > Arrays of more than one dimension would be helpful for
             | numerical work.
             | 
             | I agree with this too; it is a good idea.
             | 
             | > Standard types for 2, 3 and 4-element vectors would help
             | in graphics work.
             | 
             | This is probably helpful, too, although they can be used
             | for stuff other than graphics work as well.
             | 
             | > Do programs really have to be in monospaced fonts?
             | 
             | No, but that is due to how it is displayed and is not
             | normally a feature of the program itself. Many people
             | including myself do use monospace fonts, but this should
             | not usually be required.
             | 
             | > There's async, there are threads, and there are "green
             | threads", such as Go's "goroutines". Where's that going?
             | 
             | I had read about "green threads" and I think that it is a
             | good idea.
             | 
             | > How about "big objects?" These are separately built
             | program components which have internal state and some
             | protection from their callers. Microsoft OLE did that, some
             | .dll files do that, and Intel used to have rings of
             | protection and call gates to help with that, hardware
             | features nobody used. But languages never directly
             | supported such objects.
             | 
             | I also think it is sensible to have components that can be
             | separated from the callers, and that operating system
             | support (and perhaps hardware support) for such thing might
             | be helpful. I would design a programming language for such
             | an operating system that would directly support such
             | objects.
        
         | riffraff wrote:
         | > Source control? What's that?
         | 
         | I think squeak had Monticello for source control with their
         | image based approach almost 20+ years ago and there was
         | something else for smalltalk in the '80s too.
         | 
         | But yeah people like text and hate images, and I believe Pharo
         | switched back to some git integration.
        
           | pjmlp wrote:
           | Smalltalk implementations have had text export/import for
           | ages, and image based source control as you point out, is
           | also quite old, Monticello wasn't the first.
        
             | nahuel0x wrote:
             | In some way, Smalltalk had/has a more advanced and semantic
             | diffing called ChangeSet
        
               | igouy wrote:
               | For example, CUIS book, 9.3 The Change Set:
               | 
               | https://cuis-smalltalk.github.io/TheCuisBook/The-Change-
               | Set....
        
           | igouy wrote:
           | > something else for smalltalk
           | 
           | "ENVY/Manager augments this model by providing configuration
           | management and version control facilities. All code is stored
           | in a central database rather than in files associated with a
           | particular image. Developers are continuously connected to
           | this database; therefore changes are immediately visible to
           | all developers."
           | 
           | https://www.google.com/books/edition/Mastering_ENVY_Develope.
           | ..
           | 
           | ~
           | 
           | 1992 Product Review: Object Technology's ENVY Developer
           | 
           | http://archive.esug.org/HistoricalDocuments/TheSmalltalkRepo.
           | ..
        
         | pjmlp wrote:
         | Commercial image based systems have had source control like
         | management for decades.
         | 
         | If anything many "modern" low code SaaS products are much worse
         | in this regard, than what Lisp and Smalltalk have been offering
         | for years.
        
         | zozbot234 wrote:
         | > Capabilities ... They're not really compatible with C's flat
         | memory model ... Capabilities mean having multiple types of
         | memory
         | 
         | C is not really dependent on a flat memory model - instead, it
         | models memory allocations as separate "objects" (quite
         | reniniscent of "object orientation in hardware" which is yet
         | another name for capabilities), and a pointer to "object" A
         | cannot be offset to point into some distinct "object" B.
         | 
         | > A Truly Relational Language
         | 
         | This is broadly speaking how PROLOG and other logic-programming
         | languages work. The foundational operation in such languages is
         | a knowledge-base query, and "relations" are the unifying
         | concept as opposed to functions with predefined inputs and
         | outputs.
        
           | pjc50 wrote:
           | Possibly the nearest to applying capabilities to C is pointer
           | authentication: https://lwn.net/Articles/718888/
           | 
           | (This is one of those times where the C memory model as
           | described in the spec is very different from the mental
           | PDP-11 that C programmers actually use to reason about)
        
         | 9rx wrote:
         | _> But what 's being proposed here?_
         | 
         | He proposes that there is a need for a way to connect modules,
         | i.e. dependency injection, without the modules having explicit
         | knowledge of each other, with compile-time verification that
         | the modules being connected are compatible, without the
         | interface song and dance.
        
         | dkarl wrote:
         | > - A Language To Encourage Modular Monolith
         | 
         | > But what's being proposed here?
         | 
         | I read it, and to me it seems like they're worried about the
         | wrong things. As I understand it, they're worried about the
         | difficulty and hassle of calling unrelated code in the
         | monolith, and proposing things that would make it easier. But
         | that's wrongheaded. Monoliths don't suffer because it's too
         | hard to reuse functionality. They suffer because it's too easy.
         | Programmers create connections and dependencies that shouldn't
         | exist, and the monolith starts to strangle itself (if you have
         | good tests) or shake itself to pieces (if you don't) because of
         | unnecessary coupling. You need mechanisms that _enforce_
         | modularity, that force programmers to reuse code at designated,
         | designed module interfaces, not mechanisms that make it easier
         | to call arbitrary code elsewhere in the monolith.
         | 
         | In my opinion, a great deal of the success of microservices is
         | due to the physical impossibility of bypassing a network API
         | and calling the code of another service directly. Because of
         | this, programmers respect the importance of designing and
         | evolving APIs in microservices. Essentially, microservices
         | enforce modularity, forcing programmers to carefully design and
         | evolve the API to their code, and this is such a powerful force
         | for good design that it makes microservices appealing even when
         | their architectural aspects aren't helpful.
         | 
         | A language that made it possible to enforce modularity in a
         | monolith as effectively as it is enforced in microservices
         | would make monoliths a no-brainer when you don't need the
         | architectural aspects of microservices.
        
           | gf000 wrote:
           | This is literally the core tenet of OOP, and arguably Java
           | and friends did a decent enough job of that via encapsulation
           | and visibility modifiers.
        
         | andrewflnr wrote:
         | > - A Truly Relational Language
         | 
         | > Well, in Python, almost everything is a key/value store.
         | 
         | Why would that be anywhere near an adequate substitute? KV
         | stores are not relational, they don't support relational
         | algebra. KV stores in PLs are common as dirt, so if they were
         | relevant to the question of ending relations in a language I
         | think the author would have noticed.
        
         | igouy wrote:
         | > The OP has a good criticism of why this is a bad idea.
         | 
         | They simply assert "twiddling a run-time variable for debugging
         | in your staging environment can propagate straight into a bug
         | on production".
         | 
         | As-if straight into production without re-testing.
         | 
         | > Source control? What's that?
         | 
         | "ENVY/Manager augments this model by providing configuration
         | management and version control facilities. All code is stored
         | in a central database rather than in files associated with a
         | particular image. Developers are continuously connected to this
         | database; therefore changes are immediately visible to all
         | developers."
         | 
         | https://www.google.com/books/edition/Mastering_ENVY_Develope...
         | 
         | ~
         | 
         | 1992 Product Review: Object Technology's ENVY Developer
         | 
         | http://archive.esug.org/HistoricalDocuments/TheSmalltalkRepo...
        
         | zzo38computer wrote:
         | Many of these things (not only what you describe here but also
         | the linked article) are stuff that I had intended to be
         | available in the built-in command shell (called "Command,
         | Automation, and Query Language", which is meant to describe
         | some of the intentions) of an operating system design, so that
         | they would have support from the operating system.
         | 
         | About capabilities, I think that capabilities should be a
         | feature of the operating system, although hardware support
         | would be helpful. However, I think that it could be done with
         | tagged memory, without necessarily needing multiple types of
         | memory, and programming languages such as C could still be
         | capable of using them (although some things might not work as
         | it would be expected on other computers, e.g. if you try to
         | copy a reference to a capability into a memory area that is
         | expected to be a number and then try to perform arithmetic on
         | that number, the program is likely to crash even if the result
         | is never dereferenced).
         | 
         | However, my idea also involves "proxy capabilities" too, so
         | that you can effectively make up your own capabilities and
         | other programs receive them without necessarily knowing where
         | they came from (this allows supporting many things, including
         | (but not limited to) many of the idea of "divergent desktop" of
         | Arcan).
        
       | bruce343434 wrote:
       | As for "capabilities", I'm not sure I fully understand how that
       | is advantageous to the convention of passing the helper function
       | ("capability") as an argument to the "capable" function.
       | 
       | For instance, in Zig, you can see that a function allocates
       | memory (capability) because it requires you to pass an allocator
       | that it can call!
       | 
       | I'd like to see if others are more creative than me!
        
         | spencerflem wrote:
         | That's pretty much how it plays out, as I understand it.
         | 
         | The trick is making sure that that object is the Only possible
         | way to do the thing. And making more features like that, for
         | example Networking, or File I/O, etc
        
         | do_not_redeem wrote:
         | In Zig it's conventional to pass an allocator, but any code can
         | end run around the convention by reaching for page_allocator or
         | c_allocactor behind your back. Capabilities upgrade that
         | convention into a guarantee.
        
       | racingmars wrote:
       | > Value Database
       | 
       | > Smalltalk and another esoteric programming environment I used
       | for a while called Frontier had an idea of a persistent data
       | store environment. Basically, you could set global.x = 1, shut
       | your program down, and start it up again, and it would still be
       | there.
       | 
       | Frontier! I played with that way back when on the Mac. Fun times.
       | 
       | But as for programming language with integrated database...
       | MUMPS! Basically a whole language and environment (and, in the
       | beginning, operating system) built around a built-in global
       | database. Any variable name prefixed with ^ is global and
       | persistent, with a sparse multi-dimensional array structure to be
       | able to organize and access the variables (e.g.
       | ^PEOPLE(45,"firstname") could be "Matthew" for the first name of
       | person ID 45). Lives on today in a commercial implementation from
       | Intersystems, and a couple Free Software implementations
       | (Reference Standard M, GT.M, and the GT.M fork YottaDB). The
       | seamless global storage is really nice, but the language itself
       | is truly awful.
        
         | jonathaneunice wrote:
         | Image persistence was one of the cool ideas of Smalltalk. And
         | in practice, one of the biggest drawbacks. Cruft and old values
         | accumulated steadily, with very little way to find and
         | eliminate them. Transient execution has some cons. But on the
         | pro side, every run starts from a "clean slate."
        
           | whartung wrote:
           | This may fall in the "you think you do, but you don't
           | category", but I've always wanted a Smalltalk (or similar,
           | not that picky) with a persistent virtual memory.
           | 
           | That is, the VM is mapped to a backing file, changes
           | persisted automatically, no "saving", limited by drive space
           | (which, nowadays, is a lot). But nowadays we also have vast
           | memory space to act as a page cache and working memory.
           | 
           | My contrived fantasy use case was having a simple array name
           | "mail", which an array containing all of my email messages
           | (in email object, of course). Naturally as you get more mail,
           | the array gets longer. Also, as you delete mail, then the
           | array shifts. It's no different, roughly, than the classic
           | mbox format, save it's not just text, its objects.
           | 
           | You can see if you delete a email, from a large (several
           | GBs), there would be a lot of churn. That implies maybe it's
           | not a great idea to use that data structure, but that's not
           | the point. You CAN use that data structure if you like (just
           | like you can use mbox if you like).
           | 
           | Were it to be indexed, that would be done with parallel data
           | structures (trees or hashes or whatever).
           | 
           | But this is all done automagically. Just tweaks to pages in
           | working memory backed by the disk using the virtual memory
           | manager. Lots and lot of potential swapping. C'est la vie, no
           | different from anything else. This what happens when you map
           | 4TB into a 16GB work space.
           | 
           | The problem with such a system, is how fragile is potentially
           | is. Corrupt something and it happily persists that
           | corruption, wrecking the system. You can't reboot to fix it.
           | 
           | Smalltalk suffers from that today. Corrupt the image (oops,
           | did I delete the Object become: method again?), and its gone
           | for good. This is mitigated by having backup images, and the
           | changelist to try to bring you back to the brink but no
           | further.
           | 
           | I'm guessing a way to do that in this system is to use a copy
           | on write facility. Essentially, snapshot the persistent store
           | on each boot (or whatever), and present a list of previous
           | snapshot at start up.
           | 
           | Given the structure of a ST VM you'd like to think this is
           | not that dreadful to work up. I'd like to think a paper
           | napkin implementation PoC would be possible, just to see what
           | it's like. One of those things were the performance isn't
           | really that great, but the modern systems are so fast, we
           | don't really notice it in human terms.
           | 
           | But I do think it would be interesting.
        
             | cess11 wrote:
             | Have you looked at Pharo? Their git integration makes it
             | relatively easy to export and backup parts of your main
             | image, and to pull the things back into a fresher one once
             | you mess up.
        
             | igouy wrote:
             | Smalltalk suffers from mis-information --
             | 
             | > oops, did I delete the Object become: method again?), and
             | its gone for good.
             | 
             | And then you admit actually it's not gone for good because
             | if you created the method that will be recorded in the
             | changes.log file and if it was a provided method that will
             | still be in the provided sources file.
             | 
             | https://cuis-smalltalk.github.io/TheCuisBook/The-Change-
             | Log....
        
           | brabel wrote:
           | > with very little way to find and eliminate them.
           | 
           | The best Smalltalk these days is GlamorousToolkit:
           | https://gtoolkit.com/
           | 
           | It has a sort of git in it, so you can easily "rollback" your
           | image to previous states. So going back and forth in history
           | is trivial.
        
             | galaxyLogic wrote:
             | > you can easily "rollback" your image to previous states.
             | 
             | Sounds very interesting. Does it support multi-developer
             | merging and/or rebasing of changes?
        
               | brabel wrote:
               | I believe it's just a git repo behind the scenes. Not
               | sure if the UI exposes those things as I never used that
               | in multi-developer scenarios! Give it a go and see.
        
           | igouy wrote:
           | And in practice, both.
           | 
           |  _Save image..._ for short-term convenience; _build clean_
           | every week from archived text files.
           | 
           | ----
           | 
           | 1984 "Smalltalk-80 The Interactive Programming Environment"
           | page 500
           | 
           | "At the outset of a project involving two or more
           | programmers: Do assign a member of the team to be the version
           | manager. ... The responsibilities of the version manager
           | consist of collecting and cataloging code files submitted by
           | all members of the team, periodically building a new system
           | image incorporating all submitted code files, and releasing
           | the image for use by the team. The version manager stores the
           | current release and all code files for that release in a
           | central place, allowing team members read access, and
           | disallowing write access for anyone except the version
           | manager."
           | 
           | https://rmod-
           | files.lille.inria.fr/FreeBooks/TheInteractivePr...
        
             | jimbokun wrote:
             | So the "version manager" is a human git repository!
        
               | igouy wrote:
               | 40 years ago, yes!
               | 
               | In the same way that "network" had recently been sneaker-
               | net.
        
         | kweingar wrote:
         | The MUMPS database is wild. When I was working in MUMPS, it was
         | so easy and fun to whip up an internal tool to share with my
         | coworkers. You don't have to give any special thought at all to
         | persistence, so you're able to stay in the flow of thinking
         | about your business logic.
         | 
         | But as you said, the language itself is almost unbearable to
         | use.
        
           | danielhlockard wrote:
           | I had a professor who is responsible for a lot of the more
           | "modern" MUMPS stuff (lets be real, MUMPS is OLD!). Guy was
           | pretty unbearable too.
        
         | SeenNotHeard wrote:
         | TADS, an OOP language + VM for interactive fiction, has this
         | "value database" model. Once loaded into memory, the compiled
         | image can be updated with values stored in a separate save
         | file. The compiled image itself could store updated values as
         | well.
         | 
         | In fact, it does this during a "preinit" stage that runs
         | immediately after compilation. Once all preinit code finishes
         | executing, the compiled image is overwritten with the updated
         | state. The language includes a "transient" keyword to permit
         | creating objects that should not be stored.
         | 
         | This same mechanism permits in-memory snapshots, which are used
         | for the game's UNDO feature. No need to rewind or memento-ize
         | operations, just return to a previous state.
         | 
         | It's not a general-purpose mechanism. After all, the language
         | is for building games with multiple player-chosen save files,
         | and to permit restarting the game from a known Turn 0 state.
        
       | lihaoyi wrote:
       | Starlark, a variant of Python, can be thought of as semi dynamic:
       | all mutation in each file happens once, single threaded, and then
       | that file and all its data structures are frozen so downstream
       | files can use it in parallel
       | 
       | A lot of "staged" programs can be thought of as semi dynamic as
       | well, even things like C++ template expansion or Zig comptime:
       | run some logic up front, freeze it, then run the rest of the
       | application later
        
         | kridsdale1 wrote:
         | Google's build system uses Starlark definition files for this
         | reason. Very easy to write flexible configurations for each
         | project and module but building is of course very parallel.
        
       | kibwen wrote:
       | My wild idea is that I'd like to see a modern "high-level
       | assembler" language that doesn't have a callstack. Just like in
       | the olden days, all functions statically allocate enough space
       | for their locals. Then, combine this with some semi-convenient
       | facility for making sure that local variables for a given
       | function always fit into registers; yes, I admit that I'm strange
       | when I say that I dream of a language that forces me to do manual
       | register allocation. :P But mostly what I want to explore is if
       | it's possible to create a ""modern"" structured programming
       | language that maps cleanly to assembly, and that provides _no_
       | optimization backend at all, but has enough mechanical sympathy
       | that it still winds up fast enough to be usable.
        
         | Jtsummers wrote:
         | > all functions statically allocate enough space for their
         | locals.
         | 
         | Would you still have distinct activation records per call or
         | forfeit the ability to have reentrant functions and recursion?
         | 
         | That's one of the main reasons to move to dynamic (as in a call
         | stack) allocation of your activation records versus a single
         | static allocation per function.
        
           | kibwen wrote:
           | In this hypothetical language I'm assuming that recursion is
           | unsupported and that if threading is supported at all, then
           | each thread has its own copy of every function's locals (or
           | at least every function that can be called concurrently;
           | structured concurrency might be leveraged to prove that some
           | functions don't need to be reentrant, or maybe you just chuck
           | a mutex in each function prologue and YOLO). However, while
           | enforcing that basic recursion is forbidden isn't too
           | difficult (you make the language statically-typed, all names
           | lexically-scoped, and don't support forward declarations), it
           | does probably(?) mean that you also lose first-class
           | functions and function pointers, although I haven't thought
           | deeply about that.
        
             | vimax wrote:
             | I think lambdas or function pointers can be possible if
             | they assume all of the scope of where they are called
             | rather than where they are declared, that would prevent
             | them from allowing recursion through forward declarations.
             | 
             | It would be awkward to work with since you'd have to be
             | aware of all of the eventual caller scopes rather than your
             | current local scope when defining it.
             | 
             | I suppose it would be like macros instead of true
             | functions.
        
         | vaylian wrote:
         | Why would you like to have this language? Is it about control
         | over the execution? About better ways to personally optimize?
         | Or just intellectual pleasure? Or is it about reliving the
         | olden days of assembly language programming but with a modern
         | conveniences?
        
           | kibwen wrote:
           | I would simply find pleasure in being able to understand
           | basically every level of the stack. For a RISC architecture,
           | it's not too hard to get a grasp on how it works. Likewise
           | for a simple-enough programming language. The problem(?) is
           | that in between these two is an opaque black box--the
           | optimization backend, which I feel I have no hope of
           | understanding. So instead I wonder if it's possible to have a
           | "safe" (safer than C) and "high-level" (more abstractive than
           | C) language that is still useful and semi-performant, and I'm
           | wondering how much ergonomics would need to be sacrificed to
           | get there. It's a thought experiment.
        
         | do_not_redeem wrote:
         | Have you thought about what happens if you want to read and
         | parse a file? Do you declare the maximum filesize you want to
         | support and statically allocate that much memory?
        
           | PaulDavisThe1st wrote:
           | No, you have a scoped pointer to dynamically allocated
           | memory; when the scoped pointer is destroyed/cleaned
           | up/released at the end of the function, it releases the
           | allocated memory.
        
           | kibwen wrote:
           | I'm not intending to imply that the language I'm describing
           | can't support heap-allocated memory; Rust shows us that it's
           | even possible to do so without having to manually deallocate,
           | if you're okay with a single-ownership discipline (which is a
           | rather simple analysis to implement, as long as you don't
           | also want a borrow checker along for the ride). Instead, this
           | is about trying to make a language that makes it easy to keep
           | locals in registers/cache, rather than relying on the
           | compiler backed to do register allocation and hoping that
           | your CPU can handle all that cache you're thrashing.
        
         | jiggawatts wrote:
         | A useful purpose for such a thing is in certain embedded, hard-
         | real-time, or mission-critical scenarios.
         | 
         | Many such programming environments need strict control over
         | stack sizes to avoid _any_ possibility of stack overflow.
         | 
         | I had a similar notion a few years back, thinking about a
         | somewhat wider range of "scoped guarantees". The compiler would
         | compute things such as the maximum stack usage of a function,
         | and this would "roll up" to call sites automatically. This
         | could also be used to enforce non-usage of certain dangerous
         | features such as locks, global flags, or whatever.
        
         | pjc50 wrote:
         | > modern "high-level assembler" language that doesn't have a
         | callstack
         | 
         | PIC16 has a hardware stack of only a few return addresses, and
         | therefore imposes the "all register allocation is static" + "no
         | recursion" that you're asking for.
        
       | aziis98 wrote:
       | I agree about relational languages. It's absurd when I think that
       | SQL and Datalog came from the same foundations of relational
       | calculus. It's just so much lost expressive power.
       | 
       | I really like what PRQL [1] did, at least it makes table
       | operations easily chainable. Another one that comes to mind is
       | Datomic [2].
       | 
       | [1]: https://prql-lang.org/
       | 
       | [2]: https://docs.datomic.com/peer-tutorial/query-the-data.html
        
         | vkazanov wrote:
         | Uh... given the beauty of relational algebra I don't understand
         | how we ended up with the ugly mess of sql.
        
           | PaulHoule wrote:
           | This makes me want to throw up
           | 
           | https://www.postgresql.org/docs/current/queries-with.html
        
             | refset wrote:
             | If you would like some exposure therapy:
             | https://databasearchitects.blogspot.com/2024/12/advent-of-
             | co... [0]
             | 
             | [0] Recent discussion
             | https://news.ycombinator.com/item?id=42577736
        
               | PaulHoule wrote:
               | Some people might think it is crazy but I like wrapping
               | queries like that up in JooQ so I can write
               | recursiveQuery(table, linkClause, selectCaluse)
        
               | vkazanov wrote:
               | Life finds a way, I suppose:-)
        
           | jampekka wrote:
           | Here you go: https://www.red-gate.com/simple-
           | talk/opinion/opinion-pieces/...
           | 
           | "RM: What was key to SQL becoming the standard language for
           | relational databases in the mid- 1980s? Was all down to good
           | marketing?
           | 
           | CJD: In other words, why did SQL became so popular?
           | Especially given all its faults? Well, I think this is rather
           | a sorry story. I said earlier that there has never been a
           | mainstream DBMS product that's truly relational. So the
           | obvious question is: Why not? And I think a good way for me
           | to answer your questions here is to have a go at answering
           | this latter question in their place, which I'll do by means
           | of a kind of Q&A dialog. Like this:                   Q:
           | Why has no truly relational DBMS has ever been widely
           | available in the marketplace?         A:             Because
           | SQL gained a stranglehold very early on, and SQL isn't
           | relational.          Q:             Why does SQL have such a
           | stranglehold?          A:             Because SQL is "the
           | standard language for RDBMSs."          Q:             Why
           | did the standard endorse SQL as such and not something else-
           | something better?          A:             Because IBM
           | endorsed SQL originally, when it decided to build what became
           | DB2. IBM used to be more of a force in the marketplace than
           | it is today. One effect of that state of affairs was that-in
           | what might be seen as a self-fulfilling prophecy-competitors
           | (most especially Relational Software Inc., which later became
           | Oracle Corp.) simply assumed that SQL was going to become a
           | big deal in the marketplace, and so they jumped on the SQL
           | bandwagon very early on, with the consequence that SQL became
           | a kind of de facto standard anyway.          Q:
           | Why did DB2 support SQL?          A:             Because (a)
           | IBM Research had running code for an SQL prototype called
           | System R and (b) the people in IBM management who made the
           | decision to use System R as a basis on which to build DB2
           | didn't understand that there's all the difference in the
           | world between a running prototype and an industrial strength
           | product. They also, in my opinion, didn't understand software
           | (they certainly didn't understand programming languages).
           | They thought they had a bird in the hand.          Q:
           | Why did the System R prototype support SQL?          A:
           | My memory might be deficient here, but it's my recollection
           | that the System R implementers were interested primarily in
           | showing that a relational-or "relational"-DBMS could achieve
           | reasonable performance (recall that "relational will never
           | perform" was a widely held mantra at the time). They weren't
           | so interested in the form or quality of the user interface.
           | In fact, some of them, at least, freely admitted that they
           | weren't language designers as such. I'm pretty sure they
           | weren't all totally committed to SQL specifically. (On the
           | other hand, it's true that at least one of the original SQL
           | language designers was a key player in the System R team.)
           | Q:             Why didn't "the true relational fan club" in
           | IBM-Ted and yourself in particular-make more fuss about SQL's
           | deficiencies at the time, when the DB2 decision was made?
           | A:             We did make some fuss but not enough. The fact
           | is, we were so relieved that IBM had finally agreed to build
           | a relational-or would-be relational-product that we didn't
           | want to rock the boat too much. At the same time, I have to
           | say too that we didn't realize how truly awful SQL was or
           | would turn out to be (note that it's much worse now than it
           | was then, though it was pretty bad right from the outset).
           | But I'm afraid I have to agree, somewhat, with the criticism
           | that's implicit in the question; that is, I think I have to
           | admit that the present mess is partly my fault."
           | 
           | Discussed in HN (probably posted many times):
           | https://news.ycombinator.com/item?id=39189015
        
             | vkazanov wrote:
             | I know the story of System R really well.
             | 
             | It was a breakthrough system is almost all aspects. It
             | defined what dbs would look like both internally and
             | externally for decades.
             | 
             | In many ways SQL is like C: good for what authors wanted it
             | to be but severe consequences much later.
             | 
             | But the history doesn't care. SQL (and C) still have many-
             | many years ahead of them.
        
             | 9rx wrote:
             | _> Why has no truly relational DBMS has ever been widely
             | available in the marketplace?_
             | 
             | Postgres was "truly relational" for a significant portion
             | of its life before finally losing the battle with the SQL
             | virus. There is probably no DMBS more widely available.
             | Granted, it wasn't widely used until the SQL transition.
             | 
             |  _> SQL isn't relational._
             | 
             | This is key. Relations are too complicated for the layman,
             | the one who is paying for it, to understand. Tables are
             | more in tune to what is familiar to them. The hardcore
             | math/software nerds might prefer relationality, but they
             | aren't the ones negotiating multi-million dollar contracts
             | with Oracle/IBM.
             | 
             | I remember when Postgres moved to SQL. People started
             | billing it as being Oracle, but free. That got non-
             | technical manager attention. Without that marking success
             | appealing to the layman, I expect nobody would be using it
             | today.
        
           | 9rx wrote:
           | I have spent a lot of time trying to understand how we ended
           | up with SQL. Best I can determine, we got SQL _because_ it
           | isn 't relational, it is tablational. Tables are a lot easier
           | than relations to understand for the layman, and they
           | successfully pushed for what they were comfortable with, even
           | if to the chagrin of technical people.
        
         | mamcx wrote:
         | I explore the idea with https://tablam.org (relational +
         | array). I even toyed with making relational queries to make
         | types:                   data Person = User ?except .password
         | ?select .name, .id + Customer ?select .name, .customer_id AS
         | .id
         | 
         | So the types are in full sync when changes on the schema
         | happen. And all of this is type safe.
        
         | PaulHoule wrote:
         | I was struggling with doing interesting things with the
         | semantic web circa 2007 and was thinking "OWL sucks" and
         | looking at Datalog as an alternative. At that time Datalog was
         | an obscure topic and hard to find information about it. 10
         | years later it was big.
         | 
         | (Funny after years of searching I found somebody who taught me
         | how to do really complex modelling in OWL DL but from reading
         | the literature I'm pretty sure the average PhD or prof in the
         | field has no idea.)
        
           | leoc wrote:
           | > I found somebody who taught me how to do really complex
           | modelling in OWL DL
           | 
           | Is there any resource you could recommend for that?
        
             | PaulHoule wrote:
             | I wrote up what I learned an a technical report that got
             | sent to the editors at ISO a month or so ago and ought to
             | appear pretty soon. Look up my profile and send me a note.
        
       | PaulHoule wrote:
       | I like a lot of these ideas.
       | 
       | "Semi-dynamic" is one of the most common architectures there is
       | for large & complex systems. AAA games are usually written in a
       | combination of C++ and a scripting language. GNU Emacs is a Lisp
       | application with a custom interpreter that is optimized for
       | writing a text editor. Python + C is a popular choice as well as
       | Java + Groovy or Clojure, I've even worked with a Lua + FORTRAN
       | system.
       | 
       | I also think "parsers suck". It should be a few hundred lines at
       | most, including the POM file, to add an "unless" statement to the
       | Java compiler. You need to (1) generate a grammar which
       | references the base grammar and adds a single production, (2)
       | create a class in the AST that represents the "unless" statement
       | and (3) add an transformation that rewrites
       | unless(X) {...} -> if(!X) {...}
       | 
       | You should be able to mash up a SQL grammar and the Java grammar
       | so you can write                  var statement = <<<SELECT *
       | FROM page where id=:pageId>>>;
       | 
       | this system should be able to export a grammar to your IDE. Most
       | parser generators are terribly unergonomic (cue the event-driven
       | interface of yacc) and not accessible to people who don't have a
       | CS education (if you need a bunch of classes to represent your
       | AST shouldn't these get generated from your grammar?) When you
       | generate a parser you should get an unparser. Concrete syntax
       | trees are an obscure data structure but were used in obscure RAD
       | tools in the 1990s that would let you modify code visually and
       | make the kind of patch that a professional programmer would
       | write.
       | 
       | The counter to this you hear is that compile time is paramount
       | and there's a great case for that in large code bases. (I had a
       | system with a 40 minute build) Yet there's also a case that
       | people do a lot of scripty programming and trading compile time
       | for a ergonomics can be a win (see Perl and REBOL)
       | 
       | I think one goal in programming languages is to bury Lisp the way
       | Marc Anthony buried Caesar. Metaprogramming would be a lot more
       | mainstream if it was combined with Chomksy-based grammars,
       | supported static typing, worked with your IDE and all that.
       | Graham's _On Lisp_ is a brilliant book (read it!) that left me
       | disappointed in the end because he avoids anything involving deep
       | tree transformations or compiler theory: people do much more
       | advanced transformations to Java bytecodes. It might be easier to
       | write those kind of transformations if you had an AST comprised
       | of Java objects instead of the anarchy of nameless tuples.+
        
       | mike_hearn wrote:
       | Totally agree that programming languages are a bit stagnant, with
       | most new features being either trying to squeeze a bit more
       | correctness out via type systems (we're well into diminishing
       | returns here at the moment), or minor QoL improvements. Both are
       | useful and welcome but they aren't revolutionary.
       | 
       | That said, here's some of the feedback of the type you said you
       | didn't want >8)
       | 
       | (1) Function timeouts. I don't quite understand how what you want
       | isn't just exceptions. Use a Java framework like Micronaut or
       | Spring that can synthesize RPC proxies and you have things that
       | look and work just like function calls, but which will throw
       | exceptions if they time out. You can easily run them async by
       | using something like "CompletableFuture.supplyAsync(() ->
       | proxy.myCall(myArgs))" or in Kotlin/Groovy syntax with a static
       | import "supplyAsync { proxy.myCall(myArgs) }". You can then
       | easily wait for it by calling get() or skip past it. With virtual
       | threads this approach scales very well.
       | 
       | The hard/awkward part of this is that APIs are usually defined
       | these days in a way that doesn't actually map well to standard
       | function calling conventions because they think in terms of
       | POSTing JSON objects rather than being a function with arguments.
       | But there are tools that will convert OpenAPI specs to these
       | proxies for you as best they can. Stricter profiles that result
       | in more idiomatic and machine-generatable proxies aren't that
       | hard to do, it's just nobody pushed on it.
       | 
       | (2) Capabilities. A language like Java has everything needed to
       | do capabilities (strong encapsulation, can restrict reflection).
       | A java.io.File is a capability, for instance. It didn't work out
       | because ambient authority is needed for good usability. For
       | instance, it's not obvious how you write config files that
       | contain file paths in systems without ambient authority. I've
       | seen attempts to solve this and they were very ugly. You end up
       | needing to pass a lot of capabilities down the stack, ideally in
       | arguments but that breaks every API ever designed so in reality
       | in thread locals or globals, and then it's not really much
       | different to ambient authority in a system like the
       | SecurityManager. At least, this isn't really a programming
       | language problem but more like a standard library and runtime
       | problem.
       | 
       | (3) Production readiness. The support provided by app frameworks
       | like Micronaut or Spring for things like logging is pretty good.
       | I've often thought that a new language should really start by
       | taking a production server app written in one of these frameworks
       | and then examining all the rough edges where the language is
       | mismatched with need. Dependency injection is an obvious one -
       | modern web apps (in Java at least) don't really use the 'new'
       | keyword much which is a pretty phenomenal change to the language.
       | Needing to declare a logger is pure boilerplate. They also rely
       | heavily on code generators in ways that would ideally be done by
       | the language compiler itself. Arguably the core of Micronaut _is_
       | a compiler and it _is_ a different language, one that just
       | happens to hijack Java infrastructure along the way!
       | 
       | What's interesting about this is that you could start by forking
       | javac and go from there, because all the features already exist
       | and the work needed is cleaning up the resulting syntax and
       | semantics.
       | 
       | (4) Semi-dynamic. This sounds almost exactly like Java and its
       | JIT. Java is a pretty dynamic language in a lot of ways. There's
       | even "invokedynamic" and "constant dynamic" features in the
       | bytecode that let function calls and constants be resolved in
       | arbitrarily dynamic ways at first use, at which point they're
       | JITd like regular calls. It sounds very similar to what you're
       | after and performance is good despite the dynamism of features
       | like lazy loading, bytecode generated on the fly, every method
       | being virtual by default etc.
       | 
       | (5) There's a library called Permazen that I think gets really
       | close to this (again for Java). It tries to match the feature set
       | of an RDBMS but in a way that's far more language integrated, so
       | no SQL, all the data types are native etc. But it's actually used
       | in a mission critical production application and the feature set
       | is really extensive, especially around smooth but rigorous schema
       | evolution. I'd check it out, it certainly made me want to have
       | that feature set built into the language.
       | 
       | (6) Sounds a bit like PL/SQL? I know you say you don't want SQL
       | but PL/SQL and derivatives are basically regular programming
       | languages that embed SQL as native parts of their syntax. So you
       | can do things like define local variables where the type is
       | "whatever the type of this table column is" and things like that.
       | For your example of easily loading and debug dumping a join, it'd
       | look like this:                   DECLARE            -- Define a
       | custom record type for the selected columns            TYPE
       | EmpDept IS RECORD (               name
       | employees.first_name%TYPE,               salary
       | employees.salary%TYPE,               dept
       | departments.department_name%TYPE            );            empDept
       | EmpDept;         BEGIN            -- Select columns from the
       | joined tables into the record            SELECT e.first_name,
       | e.salary, d.department_name INTO empDept            FROM
       | employees e JOIN departments d ON e.department_id =
       | d.department_id            WHERE e.employee_id = 100;
       | -- Output the data            DBMS_OUTPUT.PUT_LINE('Name: ' ||
       | empDept.name);            DBMS_OUTPUT.PUT_LINE('Salary: ' ||
       | empDebt.salary);            DBMS_OUTPUT.PUT_LINE('Department: '
       | || emptDebt.name);         END;
       | 
       | It's not a beautiful language by any means, but if you want a
       | natively relational language I'm not sure how to make it moreso.
       | 
       | (7) I think basically all server apps are written this way in
       | Java, and a lot of client (mobile) too. It's why I think a
       | language with integrated DI would be interesting. These
       | frameworks provide all the features you're asking for already
       | (overriding file systems, transactions, etc), but you don't need
       | to declare interfaces to use them. Modern injectors like Avaje
       | Inject, Micronaut etc let you directly inject classes. Then you
       | can override that injection for your tests with a different
       | class, like a subclass. If you don't want a subtyping
       | relationship then yes you need an interface, but that seems OK if
       | you have two implementations that are really so different they
       | can't share any code at all. Otherwise you'd just override the
       | methods you care about.
       | 
       | Automatically working out the types of parameters sounds a bit
       | like Hindley-Milner type inference, as seen in Haskell.
       | 
       | (8) The common way to do this in the Java world is have an
       | annotation processor (compiler plugin) that does the lints when
       | triggered by an annotation, or to create an IntelliJ plugin or
       | pre-canned structural inspection that does the needed AST
       | matching on the fly. IntelliJ's structural searches can be saved
       | into XML files in project repositories and there's a pretty good
       | matching DSL that lets you say things like "any call to this
       | method with arguments like that and which is inside a loop should
       | be flagged as a warning", so often you don't need to write a
       | proper plugin to find bad code patterns.
       | 
       | I realize you didn't want feedback of the form "but X can do this
       | already", still, a lot of these concepts have been explored
       | elsewhere and could be merged or refined into one super-language
       | that includes many of them together.
        
         | hnlmorg wrote:
         | PL/SQL is an abomination of a language. It's easily the worst
         | example you could have given.
        
       | billytetrud wrote:
       | You might be interesting in looking at the Lima programming
       | language: http://btetrud.com/Lima/Lima-Documentation.html . It
       | has ideas that cover some of these things. For example, it's
       | intended to operate with fully automatic optimization. This
       | assumption allows shedding lots of complexity that arises from
       | needing to do the same logical thing in multiple ways that differ
       | in their physical efficiency characteristics. Like instead of
       | having 1000 different tree classes, you have 1 and optimisers can
       | then look at your code and decide what available tree structures
       | make most sense in each place. Related to your async functions
       | idea, it does provide some convenient ways of handling these
       | things. While functions are just normal functions, it has a very
       | easy way to make a block of async (using "thread") and provides
       | means of capturing async errors that result from that.
        
       | tmtvl wrote:
       | > _Some Lisps may be able to do all this, although I don't know
       | if they quite do what I'm talking about here; I'm talking about
       | there being a very distinct point where the programmer says "OK,
       | I'm done being dynamic" for any given piece of code._
       | 
       | In Common Lisp there are tricks you can pull like declaring
       | functions in a lexical scope (using labels or flet) to remove
       | their lookup overhead. But CL is generally fast enough that it
       | doesn't really matter much.
        
         | kazinator wrote:
         | You can declaim inline a toplevel function. That doesn't
         | necessarily mean that it will be integrated into callers. Among
         | the possible effects is that the dynamism of reference can be
         | culled away. If a function A calls B where B is declaimed
         | inline then A can be compiled to assume _that_ B definition.
         | (Such that if B is redefined at run-time, A can keep calling
         | the old B, not going through the # 'B function binding
         | lookup.).
         | 
         | I seem to remember that Common Lisp compilers are allowed to do
         | this for functions that are in the same file even if they are
         | not declaimed inline. If A and B are in the same file, and B is
         | not declaimed _notinline_ (the opposite of _inline_ ), then A
         | can be translated to assume the B definition.
         | 
         | So all your helper functions in a Lisp module are allowed to be
         | called more efficiently, not having to go through the function
         | binding of the symbol.
        
       | fanf2 wrote:
       | Interesting that E is cited under "capabilities", but not under
       | "loosen up the functions". E's eventual-send RPC model is
       | interesting in a number of ways. If the receiver is local then it
       | works a bit like a JavaScript callback in that there's an event
       | loop driving execution; if it's remote then E has a clever
       | "promise pipelining" mechanism that can hide latency. However E
       | didn't do anything memorable (to me at least!) about handling
       | failure, which was the main point of that heading.
       | 
       | For "capabilities" and "A Language To Encourage Modular
       | Monoliths", I like the idea of a capability-secure module system.
       | Something like ML's signatures and functors, but modules can't
       | import, they only get access to the arguments passed into a
       | functor. Everything is dependency injection. The build system
       | determines which modules are compiled with which dependencies
       | (which functors are passed which arguments).
       | 
       | An existing "semi-dynamic language" is CLOS, the Common Lisp
       | object system. Its metaobject protocol is designed so that there
       | are clear points when defining or altering parts of the object
       | system (classes, methods, etc.) at which the result is compiled,
       | so you know when you pay for being dynamic. It's an interesting
       | pre-Self design that doesn't rely on JITs.
       | 
       | WRT "value database", a friend of mine used to work for a company
       | that had a Lisp-ish image-based geospatial language. They were
       | trying to modernise its foundations by porting to the JVM. He had
       | horror stories about their language's golden image having
       | primitives whose implementation didn't correspond to the source,
       | because of decades of mutate-in-place development.
       | 
       | The most common example of the "value database" or image-based
       | style of development is in fact your bog standard SQL database:
       | DDL and stored procedures are very much mutate-in-place
       | development. We avoid the downsides by carefully managing
       | migrations, and most people prefer not to put lots of cleverness
       | into the database. The impedance mismatch between database
       | development by mutate-in-place and non-database development by
       | rebuild and restart is a horribly longstanding problem.
       | 
       | As for "a truly relational language", at least part of what they
       | want is R style data frames.
        
       | Joel_Mckay wrote:
       | Marklar
       | 
       | https://www.youtube.com/watch?v=BSymxjrzdXc
       | 
       | I found it amusing most of the language is supposedly
       | contextually polymorphic by definition. =3
        
       | stuhood wrote:
       | We have built something that hits on points 1, 3, 5, and 7 at
       | https://reboot.dev/ ... but in a multi-language framework
       | (supporting Python and TypeScript to start).
       | 
       | The end result is something that looks a lot like distributed,
       | persistent, transactional memory. Rather than explicit
       | interactions with a database, local variable writes to your state
       | are transactionally persisted if a method call succeeds, even
       | across process/machine boundaries. And that benefits point 7,
       | because transactional method calls compose across
       | team/application boundaries.
       | 
       | [1] Loosen Up The Functions [3] Production-Level Releases [5]
       | Value Database [7] A Language To Encourage Modular Monoliths
        
         | masfuerte wrote:
         | This seems to be similar to Azure Durable Functions:
         | 
         | https://learn.microsoft.com/en-us/azure/azure-functions/dura...
        
           | stuhood wrote:
           | They are related, for sure. But one of the biggest
           | differences is that operations affecting multiple Reboot
           | states are transactional, unlike Azure's "entity functions".
           | 
           | Because multiple Azure entity functions are not updated
           | transactionally, you are essentially always implementing the
           | saga pattern: you have to worry about cleaning up after
           | yourself in case of failure.
           | 
           | In Reboot, transactional function calls automatically roll
           | back all state changes if they fail, without any extra
           | boilerplate code. Our hypothesis is that that enables a large
           | portion of an application to skip worrying about failure
           | entirely.
           | 
           | Code that has side-effects impacting the outside world can be
           | isolated using our workflow mechanism (effectively durable
           | execution), which can themselves be encapsulated inside of
           | libraries and composed. But we don't think that that is the
           | default mode that developers should be operating in.
        
             | gf000 wrote:
             | Could you expand on this part?
             | 
             | > Code that has side-effects impacting the outside world
             | can be isolated using our workflow mechanism (effectively
             | durable execution
             | 
             | Sounds very interesting!
             | 
             | I have been thinking about something like this for a new
             | PL, and many kinds of side effect can actually be reversed,
             | as if it never happened.
             | 
             | I have also read that exceptions can complicate control
             | flow, disallowing some optimizations - but if they are
             | transactional, then we can just add their reverse to the
             | supposedly already slow error path, and enjoy our
             | performance boost!
        
       | cpeterso wrote:
       | The section about language support for modular monoliths reminds
       | me of John Lakos's _" Large-Scale C++ Software Design"_, which
       | focuses on the physical design/layout of large C++ projects to
       | enforce interfaces and reduce coupling and compilation time.
       | Example recommendations include defining architecture layers
       | using subdirectories and the PImpl idiom. It's pretty dated
       | (1996, so pre-C++98), but still a unique perspective on an
       | overlooked topic.
       | 
       | https://www.amazon.com/dp/0201633620
        
       | dboreham wrote:
       | Folks, this is not a process that converges. We've now had 60
       | years of language design, use and experience. We're not going to
       | get to an ideal language because there are (often initially
       | hidden) tradeoffs to be made. Everyone has a different idea of
       | which side of each tradeoff should be taken. Perhaps in the
       | future we can get AI to generate and subsequently re-generate
       | code, thereby avoiding the need to worry too much about language
       | design (AI doesn't care that it constantly copies/pastes or has
       | to refactor all the time).
        
         | chikere232 wrote:
         | Practically it has kinda converged, though not necessarily on a
         | global optimum. Most coding is done in relatively few
         | languages.
        
           | cmontella wrote:
           | It's bimodal. Most of the world uses one paradigm of
           | programming (declarative programming via Excel and SQL),
           | while developers use another paradigm (imperative programming
           | via Python/C/C++/Javascript et al.).
        
       | TrianguloY wrote:
       | I'll throw another idea here I've been thinking from a time now.
       | 
       | Most languages have a while construct and a do-while.
       | while(condition){block};       do{block}while(condition);
       | 
       | The while is run as                   ...       start:
       | condition         branch-if-false > end         block
       | branch-always > start       end:         ...
       | 
       | And the do-while switches the order:                   ...
       | start:         block         condition         branch-if-true >
       | start         ...
       | 
       | The issue with the while is that more often than not you need to
       | do some preparations before the condition. So you need to move
       | that to a function, or duplicate it before and inside the loop.
       | Do-while doesn't help, since with that you can't do anything
       | after the condition. The alternative is a while(true) with a
       | condition in the middle.                 while(true){
       | prepare;         if(!check) break;         process       }
       | 
       | But what if there was a language construct for this? Something
       | like                 do{prepare}while(condition){process}
       | 
       | Is there a language that implements this somehow? (I'm sure there
       | is, but I know no one)
       | 
       | The best thing is that this construct can be optimized in
       | assembly perfectly:                   ...         jump-always >
       | start       after:         process       start:         prepare
       | condition         branch-if-true > after         ...
        
         | smitty1e wrote:
         | At what point are you just doing async and coroutines?
        
         | do_not_redeem wrote:
         | Interesting idea, but once you add scoping:                 do
         | {           let value = prepare();       } while
         | (value.is_valid) {           process(value);       }
         | 
         | Can the second block of the do-while see `value` in its lexical
         | scope? If yes, you have this weird double brace scope thing.
         | And if no, most non-trivial uses will be forced to fall back to
         | `if (...) break;` anyway, and that's already clear enough imo.
        
           | TrianguloY wrote:
           | The scope should be unique, yes. In your example value should
           | be visible.
           | 
           | Your are right about the word double braces, but I can't
           | think of an alternate syntax other than just removing the
           | braces around the while. But in that case it may seem odd to
           | have a keyword that can only be used inside a specific
           | block...wich is basically a macro for a if(.)break; Maybe I'm
           | too used to the c/java syntax, maybe with a different way of
           | defining blocks?
        
           | adamc wrote:
           | That seems more like a programmer expectations issue than
           | something fundamental. Essentially, you have "do (call some
           | function that returns a chunk of state) while (predicate that
           | evaluates the state) ..."
           | 
           | Hard to express without primitives to indicate that, maybe.
        
           | PaulDavisThe1st wrote:
           | Let me FTFY:                 do {           let value =
           | prepare();           while (value.is_valid) {
           | process(value);       }
        
           | coldtea wrote:
           | > _Can the second block of the do-while see `value` in its
           | lexical scope? If yes, you have this weird double brace scope
           | thing_
           | 
           | As long as it's documented and expected, it's not weird.
           | 
           | The scope then is the whole "do-while" statement, not the
           | brace.
        
         | digging wrote:
         | I don't write a lot of while loops so this is just a bit
         | unfamiliar to me, but I'm not really understanding how this
         | isn't the same as `do{block}while(condition);`? Could you give
         | a simple example of what kind of work `prepare` is doing?
        
           | TrianguloY wrote:
           | Think of a producer (a method that returns data each time you
           | request one, like reading a file line by line or extracting
           | the top of a queue for example) that you need to parse and
           | process until you find a special element that means "stop".
           | 
           | Something like                 do{
           | raw_data=produce.get()         data=parse(raw_data)
           | }while(data!=STOP){         process(data)       }
           | 
           | I'm aware this example can be trivially replaced with a
           | while(data=parse(producer.get())){process(data)} but you are
           | forced to have a method, and if you need both the raw and
           | parsed data at the same time, either you mix them into a
           | wrapper or you need to somehow transfer two variables at the
           | same time from the parse>condition>process
           | 
           | A do-while here also has the same issue, but in this case
           | after you check the condition you can't do any processing
           | afterwards (unless you move the check and process into a
           | single check_and_process method...which you can totally do
           | but again the idea is to not require it)
        
           | bandie91 wrote:
           | i often want to write:                 do {         offset =
           | tell(filehandle)       } while(line = readline(filehandle))
           | {         print "line starts at offset: ", offset       }
        
         | jolmg wrote:
         | You mean like a shell's while-do-done? It's just about allowing
         | statements as the conditions, rather than just a single
         | expression. Here's an example from a repl I wrote:
         | repl_prompt="${repl_prompt-repl$ }"       while         printf
         | "%s" "$repl_prompt"         read -r line       do         eval
         | "$line"       done       echo
         | 
         | The `printf` is your `prepare`.
         | 
         | This should also be doable in languages where statements are
         | expressions, like Ruby, Lisp, etc.
         | 
         | Here's a similar Ruby repl:                 while (
         | print "repl> "         line = gets       ) do         result =
         | eval line         puts result.inspect       end       puts
        
           | TrianguloY wrote:
           | Exactly, here you are basically keeping it as a while with a
           | condition but allowing it to be any code that at the end
           | returns a boolean, although you need to make sure that
           | variables defined in that block can be used in the do part.
           | 
           | Sidenote: I wasn't aware that shell allows for multiple
           | lines, good to know!
        
         | url00 wrote:
         | Not quite the same but almost feels like the BEGIN block in
         | awk.
        
         | brabel wrote:
         | I suspect you'll love Common Lisp's LOOP:
         | https://gigamonkeys.com/book/loop-for-black-belts
         | 
         | Example:                   (loop repeat 8                    ;;
         | init x, then decide how to "prepare" next  iteration
         | for x = 0 then (+ (* 2 x) 1)                    collect x)
         | (0 1 3 7 15 31 63 127)
         | 
         | You can insert a condition check in the middle, of course:
         | (loop repeat 8                    for x = 0 then (+ (* 2 x) 1)
         | ;; but stop if x gets too big                    while (< x
         | 100)                    collect x)              (0 1 3 7 15 31
         | 63)
         | 
         | And much, much more. It's the ultimate loop construct.
        
           | gpderetta wrote:
           | Lambda The Ultimate Loop!
        
           | lispm wrote:
           | actually, according to the LOOP syntax, the REPEAT clause has
           | to follow the FOR clause...
        
             | brabel wrote:
             | Just a silly example, but it does work on SBCL at least.
        
               | lispm wrote:
               | A bunch of things work in implementations, while but are
               | not standard conforming.
        
           | sparkie wrote:
           | Don't forget about the `prog*` family.
           | 
           | ---                   (prog1 foo bar*)
           | 
           | Evaluates foo, then bar(s), and returns the result of
           | evaluating foo and discards the results of bar(s).
           | 
           | Useful if `foo` is the condition and you need to perform some
           | change to it immediately after, eg:                   (while
           | (prog1 (< next prev) (setq prev next)) ...)
           | 
           | ---                   (prog2 foo bar baz*)
           | 
           | Evaluates foo, then bar, then baz(s) (if present), returns
           | the result of evaluating bar and discards the results of
           | evaluating foo and baz(s).
           | 
           | Might be what GP wants. `foo` is the preparation, `bar` is
           | the condition`, and `baz` can be some post-condition mutation
           | on the compared value. Not too dissimilar to
           | for (pre, cond, post) {}
           | 
           | With `prog2` you could achieve similar behavior with no built
           | in `for`:                   (while (prog2 pre cond post) ...)
           | 
           | ---                   (progn foo*)
           | 
           | Evaluate each foo in order, return the result of evaluating
           | the last element of foo and discard all the others.
           | 
           | `progn` is similar to repeated uses of the comma operator in
           | C, which GP has possibly overlooked as one solution.
           | while (prepare, condition) { process }
        
           | taeric wrote:
           | Learning to embrace the LOOP construct has been an
           | experience, for me. Same with the FORMAT abilities. It is
           | amazing how much hate they both get, for how capable they
           | both are.
        
         | tajpulo wrote:
         | I think we have a similar way of thinking. I once wrote a blog
         | post about a for loop extension (based on Golang for
         | illustration) [0].                 values := []string{"hello",
         | "world"}       for v, value := range values {
         | fmt.Printf("%s", value);       }       inter {
         | fmt.Printf(",")       }       before {         fmt.Printf("[")
         | }       after {         fmt.Println("]")       }       empty {
         | fmt.Println("(nothing found)")       }
         | 
         | [0] https://lukas-prokop.at/articles/2024-04-24-for-loop-
         | extensi...
        
         | cdhdc wrote:
         | Eiffel has the loop structure.
         | 
         | from <initialization statements> until <termination condition>
         | loop <group of statements> end
        
         | teo_zero wrote:
         | In some way it's the dual of break, in that you want to jump
         | into the middle of the loop, while break is to jump out of it.
         | 
         | Let's rewrite the loop this way, with 'break' expanded to
         | 'goto':                 while (true) {         prepare...
         | if (!cond) goto exitpoint;         process...       }
         | exitpoint:
         | 
         | The dual would be:                 goto entrypoint;       do {
         | process...       entrypoint:         prepare...       }
         | while(cond);
         | 
         | Both constructs need two points: where the jump begins and
         | where it lands. The 'break' is syntactic sugar that removes the
         | need to specify the label 'exitpoint'. In fact with 'break' the
         | starting point is explicit, it's where the 'break' is, and the
         | landing point is implicit, after the closing '}'.
         | 
         | If we want to add the same kind of syntactic sugar for the
         | jump-in case, the landing point must be explicit (no way for
         | the compiler to guess it), so the only one we can make implicit
         | is the starting point, that is where the 'do' is.
         | 
         | So we need: a new statement, let's call it 'entry', that is the
         | dual of 'break' and a new semantic of 'do' to not start the
         | loop at the opening '{' but at 'entry'.                 do {
         | process...         entry;         prepare...       } while
         | (cond);
         | 
         | Is it more readable than today's syntax? I don't know...
        
         | scotty79 wrote:
         | When we are on subject of loops... I'd love to have 'else'
         | block for loops that runs when the loop had zero iterations.
        
           | fuzztester wrote:
           | Not the same thing (although I thought it was), according to
           | the Python docs, but related:
           | 
           | https://docs.python.org/3/reference/compound_stmts.html
           | 
           | See sections 8.3, the for statement, and 8.2, the while
           | statement.
        
             | scotty79 wrote:
             | Yeah, `while...else` in Python does the wrong thing.
             | Executes `else` block when the loop finished normally (not
             | through `break`).
             | 
             | Scala for example has a `breakable {}` block that lets you
             | indicate where you should land after a `break`
             | breakable {           while condition {             // loop
             | body             if otherCondition then break;
             | // rest of the body           }           // body of
             | pythonic else block         } // you land here if you break
             | 
             | However I have no idea how to implement the kind of `else`
             | I described in any language without checking the condition
             | twice.
        
         | scotty79 wrote:
         | In Scala you can do:                   while { prepare;
         | condition }          do { process }
         | 
         | This runs all 3 in order every iteration but quits if condition
         | evaluates to false. It just uses the fact that value of a block
         | is the value of the last expression in the block.
         | 
         | Scala has a lot of syntax goodies although some stuff is
         | exotic. For example to have a 'break' you need to import it and
         | indicate where from exactly you want to break out of.
        
         | int_19h wrote:
         | C-style for-loop is kinda sorta this. Although the "prepare"
         | part has to be an expression rather than a statement, given
         | that you have the comma operator and ?: you can do a lot there
         | even in C. In C++, you can always stick a statement in
         | expression context by using a lambda. So:                   for
         | ([]{             /*prepare*/         }(); /*condition*/;) {
         | /*body*/         }
         | 
         | However, the most interesting take on loops that I've seen is
         | in Sather, where they are implemented on top of what are,
         | essentially, coroutines, with some special facilities that make
         | it possible to exactly replicate the semantics of the usual
         | `while`, `break` etc in this way:
         | https://www.gnu.org/software/sather/docs-1.2/tutorial/iterat...
        
         | Mikhail_Edoshin wrote:
         | The C language construct for that is 'goto'.
        
           | coldtea wrote:
           | Yeah, but the parent wants a non brain-damaged too-general
           | construct
        
             | Mikhail_Edoshin wrote:
             | What if loops are a design mistake?
             | 
             | Look at C: it has 5 loop-related keywords (4.5; 'break' is
             | two-timing in 'switch') yet it is still not enough.
        
         | drpixie wrote:
         | Ada has had something similar and very flexible since from the
         | 80s ... like:                 loop
         | Get(Current_Character);       exit when Current_Character =
         | '*';         Echo(Current_Character);       end loop;
         | 
         | There's not that much new under the prog lang sun :(
        
           | vrighter wrote:
           | do {             Get(Current_Character);             if
           | (Current_Character == '*') break;
           | print(Current_Character);         } while (true);
           | 
           | I don't see why this needs a new construct in languages that
           | don't already have it. It's just syntactic sugar that doesn't
           | actually save any work. The one with the specialized
           | construct isn't really any shorter and looks pretty much the
           | same. Both have exactly one line in the middle denoting the
           | split. And both lines look really similar anyway.
        
         | jiggawatts wrote:
         | PowerShell can process 0..n input objects from the pipeline
         | using BEGIN {...} PROCESS {...} END {...} blocks.
         | 
         | I find this so incredibly useful, that I miss it from other
         | languages.
         | 
         | Something related that I've noticed with OO languages such as
         | Java is that it tends to result in "ceremony" getting repeated
         | n-times for processing n objects. a well-designed begin-
         | process-end syntax for function calls over iterables would be
         | amazing. This could apply to DB connection creation, security
         | access checks, logging, etc...
        
         | Joker_vD wrote:
         | Well, I am in a process of making a language where general
         | loops will look like                   loop
         | prepare;             while check;             process;
         | end;
         | 
         | I also think you'd enjoy Knuth's article "Structured
         | Programming with go to Statements" [0]. It's the article that
         | gave us the "premature optimization is the root of all evil"
         | quote but it's probably the least interesting part of it. Go
         | read it, it has a several sections that discuss looping
         | constructs and possible ways to express it.
         | 
         | [0] https://pic.plover.com/knuth-GOTO.pdf
        
         | klibertp wrote:
         | Raku can do this with Phasers:
         | https://docs.raku.org/language/phasers and last/next/redo:
         | https://docs.raku.org/language/phasers
         | 
         | Though, you still need all that to be nested inside the loop (a
         | closure, more generally.)
        
       | andyferris wrote:
       | For semi-dynamic language, Julia definitely took the approach of
       | being a dynamic language that can be (and is) JITed to excellent
       | machine code. I personally have some larger projects that do a
       | lot of staged programming and even runtime compilation of user-
       | provided logic using Julia. Obviously the JIT is slower to
       | complete than running a bit of Lua or whatever, but the speed
       | after that is phenomenal and there's no overhead when you run the
       | same code a second time. It's pretty great and I'd love to see
       | more of that ability in other languages!
       | 
       | Some of the other points resonate with me. I think sensible
       | dynamic scoping would be an easy way to do dependency injection.
       | Together with something like linear types you could do
       | capabilities pretty smoothly, I think. No real reason why you
       | couldn't experiment with some persistent storage as one of these
       | dependencies, either. Together with a good JIT story would make
       | for a good, modular environment.
        
         | andyferris wrote:
         | Oh and Zig is another option for allowing injections that are
         | checked when used at a call site rather than predefined through
         | interfaces.
         | 
         | AFAIK it doesn't have closures (it's too C-like) so you need to
         | use methods for all your (implicit) interfaces, but that's
         | okay...
         | 
         | I think the "exemplars" could be automatically yoinked from
         | documentation and tests and existing usage of the function in
         | the code base. Work needs to be done on the IDE front to make
         | this accessible to the user.
        
       | gpderetta wrote:
       | Well OP, are you me? everything you listed is also in my short
       | wishlist for a programming language (well except for the value
       | database, once to you have first class relational tables in your
       | language, persistence can be tied to the table identity, doesn't
       | need to be implicit).
       | 
       | Capabilities and dynamic scoping for "modularisation" nicely lead
       | to implicit variables instead of truly global dynamically scoped
       | variables. Implicit variables also probably work well to
       | implement effect systems which means well behaved asyncs.
       | 
       | Edit: other features I want:
       | 
       | - easy embedding in other low level languages (c++ specifically)
       | 
       | - conversely, easy to embed functions written in another language
       | (again c++).
       | 
       | - powerful, shell-like, process control system (including process
       | trees and pipelines), including across machines.
       | 
       | - built-in cooperative shared memory concurrency, and preemptive
       | shared nothing.
       | 
       | - built-in distributed content addressed store
       | 
       | I guess I want Erlang :)
        
       | losvedir wrote:
       | I love these ideas! I've been thinking about the "fully
       | relational" language ever since I worked with some product folks
       | and marketers at my start up 15 years ago who "couldn't code" but
       | were wizards at cooking up SQL queries to answer questions about
       | what was going on with our users and product. There was a
       | language written in rust, Tablam[0] that I followed for a while,
       | which seemed to espouse those ideas, but it seems like it's not
       | being owrked on anymore. And Jamie from Scattered Thoughts[1] has
       | posted some interesting articles in that direction as well. He
       | used to work on the old YC-company/product LightTable or Eve or
       | something, which was in the same space.
       | 
       | I've also always thought Joe Armstrong's (RIP) thought of "why do
       | we need modules" is really interesting, too. There's a language
       | I've seen posted on HN here a couple times that seems to go in
       | that approach, with functions named by their normalized hash
       | contents, and referred to anywhere by that, but I can't seem to
       | remember what it's called right now. Something like "Universe" I
       | think?
       | 
       | [0] https://github.com/Tablam/TablaM [1] https://www.scattered-
       | thoughts.net [2] https://erlang.org/pipermail/erlang-
       | questions/2011-May/05876...
        
         | stux wrote:
         | > with functions named by their normalized hash contents, and
         | referred to anywhere by that, but I can't seem to remember what
         | it's called right now. Something like "Universe" I think?
         | 
         | Unison: https://www.unison-lang.org/docs/the-big-idea/
        
           | losvedir wrote:
           | Aha! That's it, thanks.
        
       | cmontella wrote:
       | > A Truly Relational Language... Value Database
       | 
       | I helped on a language called Eve about 10 years ago. A truly
       | relational language was exactly what that language was supposed
       | to be, or at least that's what we were aiming at as a solution
       | for a user-centric programming language.
       | 
       | https://witheve.com
       | 
       | The language we came up with was sort of like Smalltalk + Prolog
       | + SQL. Your program was a series of horn clauses that was backed
       | by a Entity-Attribute-Value relational database. So you could
       | write queries like "Search for all the clicks and get those whose
       | target is a specific id, then as a result create a new fact that
       | indicates a a button was pressed. Upon the creation of that fact,
       | change the screen to a new page". We even played around with
       | writing programs like this in natural language before LLMs were a
       | thing (you can see some of that here
       | https://incidentalcomplexity.com/2016/06/10/jan-feb/)
       | 
       | Here's a flappy bird game written in that style:
       | https://play.witheve.com/#/examples/flappy.eve
       | 
       | It's very declarative, and you have to wrap you brain around the
       | reactivity and working with collections of entities rather than
       | individual objects, so programming this way can be very
       | disorienting for people used to imperative OOP langauges.
       | 
       | But the results are that programs are much shorter, and you get
       | the opportunity for really neat tooling like time travel
       | debugging, where you roll the database back to a previous point;
       | "what-if" scenarios, where you ask the system "what would happen
       | if x were y" and you can potentially do that for many values of
       | y; "why not" scenarios, where you ask the system why a value was
       | not generated; value providence, where you trace back how a value
       | was generated... this kind tooling that just doesn't exist with
       | most languages due to how they languages are built to throw away
       | as much information away as possible on each stage of
       | compilation. The kind of tooling I'm describing requires keeping
       | and logging information about your program, and then leveraging
       | it at runtime.
       | 
       | Most compilers and runtimes throw away that information as the
       | program goes through the compilation process and as its running.
       | There is a cost to pay in terms of memory and speed, but I think
       | Python shows that interpretation speed is not that much of a
       | barrier to language adoptions.
       | 
       | But like I said, that was many years ago and that team has
       | disbanded. I think a lot of what we had in Eve still hasn't
       | reached mainstream programming, although some of what we were
       | researching found its way into Excel eventually.
       | 
       | > Loosen Up The Functions... Capabilities... Production-Level
       | Releases... Semi-Dynamic Language... Modular Monoliths
       | 
       | I really like where the author's head at, I think we have similar
       | ideas about programming because I've been developing a language
       | called Mech that fits these descriptors to some degree since Eve
       | development was shut down.
       | 
       | https://github.com/mech-lang/mech
       | 
       | So this language is not supposed to be relational like Eve, but
       | it's more like Matlab + Python + ROS (or Erlang if you want to
       | keep it in the languages domain).
       | 
       | I have a short 10 min video about it here:
       | https://www.hytradboi.com/2022/i-tried-rubbing-a-database-on...
       | (brief plug for HYTRADBOI 2025, Jamie also worked on Eve, and if
       | you're interested in the kinds of thing the author is, I'm sure
       | you'll find interesting videos at HYTRADBOI '22 archives and
       | similarly interested people at HYTRADBOI '25), but this video is
       | out of date because the language has changed a lot since then.
       | 
       | Mech is really more of a hobby than anything since I'm the only
       | one working on it aside from my students, who I conscript, but if
       | anyone wants to tackle some of these issues with me I'm always
       | looking for collaborators. If you're generally interested in this
       | kind of stuff drop by HYTRADBOI, and there's also the Future Of
       | Coding slack, where likeminded individuals dwell:
       | https://futureofcoding.org. You can also find this community at
       | the LIVE programming workshop which often coincides with SPLASH:
       | https://liveprog.org
        
         | BoiledCabbage wrote:
         | I remember both LightTable and Eve. At the time I thought they
         | were both really interesting ideas but wasn't sure where they
         | were going.
         | 
         | Re-reading the eve website now, with 10+ years more experience
         | and understanding of languages I'm really astounded at how
         | brilliant Eve was, and how far ahead of it's time it was (and
         | still is). Also at how rare it is to have any revolutionary
         | ideas in modern programming language design make it out of
         | theory in contemporary times. There were many radical ideas in
         | the 60 and 70s, but so much now is incremental.
         | 
         | It's a shame Eve couldn't continue, just to see what it
         | would've become and the influence it would have had on language
         | expectations. Really cool stuff in there. While not likely, I
         | hope someone picks up those ideas and continues them.
         | 
         | Did the effort just run out of funding? Or did it hit a
         | stumbling block?
        
           | cmontella wrote:
           | Thanks for the kind words, and I agree it is a shame, but we
           | a ran out of money! Chris raised $2m from investors, and we
           | spent that over 3 years with a pretty minimal burn rate as
           | far as SF startups go. We couldn't really show a path to
           | making money at that time (although I still think we were on
           | to some things), so we couldn't raise anymore. We tried for
           | an acquihire but nah.
           | 
           | As for continuing the ideas, I'm putting a lot of what we
           | learned into Mech. I know that Chris and Josh went to work at
           | Relational AI, but I'm not sure exactly what work they got up
           | to there. But Chris recently posted about generative AI and
           | how it goes back to things we were thinking about in 2015
           | with respect to Eve:
           | https://x.com/ibdknox/status/1630548754238435330
           | 
           | So in that sense generative AI can be a vector for some of
           | the ideas in Eve coming to the mainstream.
        
       | int_19h wrote:
       | As far as "semi-dynamic" goes, C# has an interesting take coming
       | from the other direction - i.e. a fully statically typed language
       | originally bolting dynamic duck typing later on.
       | 
       | It's done in a way that allows for a lot of subtlety, too.
       | Basically you can use "dynamic" in lieu of most type annotations,
       | and what this does is make any dispatch (in a broad sense - this
       | includes stuff like e.g. overload resolution, not just member
       | dispatch) _on that particular value_ dynamic, but without
       | affecting other values involved in the expression.
        
         | gpderetta wrote:
         | > in a broad sense - this includes stuff like e.g. overload
         | resolution, not just member dispatch
         | 
         | It specifically allows for multiple dispatch that is not even
         | available in most dynamic languages not called lisp!
        
       | rednafi wrote:
       | "It feels like programming languages are stagnating."
       | 
       | As they should be. Not every language needs to turn into C++,
       | Rust, Java, C#, or Kotlin.
       | 
       | The only group I see lamenting about features these days are PL
       | theorists, which is fine for research languages that HN loves but
       | very few use outside the bubble.
       | 
       | Some of us like the constraints of C or Go.
        
       | davidw wrote:
       | Thanks - this was one of the more interesting things I've read
       | here in a while.
       | 
       | I wonder if "Programming languages seem to have somewhat
       | stagnated to me.", a sentiment I share, is just me paying less
       | attention to them or a real thing.
        
         | Lerc wrote:
         | I think there is innovation, but there's more than innovation
         | required to be a good language. If a innovative feature is the
         | cornerstone of a language, it frequently means that the
         | language neglects pragmatic coding features that while not
         | particularly special contribute to the language being nice to
         | use.
         | 
         | I feel like in the next few years in languages will be things
         | like Rust descendants where people with experience in using
         | Rust want to keep what works for them but scales back some of
         | the rigidity in favour of pragmatism.
         | 
         | It's also with noting that there are existing languages that
         | are also changing over time. Freepascal has developed a lot of
         | features over the years that make it fairly distant from
         | original Pascal. More recent languages like Haxe are still
         | developing into their final form. TypeScript has gone from a
         | language that provided a tangible solution to an existing
         | problem to a quagmire of features that I'd rather not have.
        
       | liontwist wrote:
       | > solve the problem by making all function calls async.
       | 
       | This is just blocking code and it's beautiful.
        
       | OkayPhysicist wrote:
       | An interesting problem I've played around with fair bit is the
       | idea of a maximally expressable non-Turing complete language,
       | trying to make a language that is at least somewhat comfortable
       | to use for many tasks, while still being able to make static
       | assertions about runtime behavior.
       | 
       | The best I've managed is a functional language that allows for
       | map, filter, and reduce, but forbids recursion or any other
       | looping or infinite expansion in usercode.
       | 
       | The pitch is that this kind of language could be useful in
       | contexts where you're executing arbitrary code provided by a
       | potentially malicious third party.
        
         | tjalfi wrote:
         | If you're interested in prior art, Ian Currie's _NewSpeak_ was
         | an attempt at a non-Turing complete language for safety
         | critical systems. Most of the search results are for a
         | different language with the same name, but  "RSRE currie
         | newspeak" should find relevant links.
        
         | BriggyDwiggs42 wrote:
         | Could be a good idea for a multiplayer ingame scripting
         | language.
        
         | ameliaquining wrote:
         | I think you're asking for Starlark (https://starlark-lang.org),
         | a language that strongly resembles Python but isn't Turing-
         | complete, originally designed at Google for use in their build
         | system. There's also Dhall (https://dhall-lang.org), which
         | targets configuration use cases; I'm less familiar with it.
         | 
         | One problem is that, while non-Turing-completeness can be
         | helpful for maintainability, it's not really sufficient for
         | security. Starlark programs can still consume exponential
         | amounts of time and memory, so if you run an adversary's
         | Starlark program without sandboxing it, you're just as
         | vulnerable to denial-of-service attacks as you'd be with a
         | Turing-complete language. The most common solution is
         | sandboxing, wherein you terminate the program if it exceeds
         | time or memory limits; however, once you have that, it's no
         | longer necessary for the language to not be Turing-complete, so
         | you might as well use a popular mainstream language that's easy
         | to sandbox, like JavaScript.
         | 
         | One other intriguing option in the space is CEL
         | (https://cel.dev), also designed at Google. This targets use
         | cases like policy engines where programs are typically small,
         | but need to be evaluated frequently in contexts where
         | performance matters. CEL goes beyond non-Turing-completeness,
         | and makes it possible to statically verify that a program's
         | time and space complexity are within certain bounds. This,
         | combined with the lack of I/O facilities, makes it safe to run
         | an adversary's CEL program outside a sandbox.
        
         | layer8 wrote:
         | Non-Turing-completeness doesn't buy you that much, because you
         | can still easily multiply runtime such that it wouldn't
         | terminate within your lifetime. With just _map_ you can
         | effectively build the cross product of a list with itself. Do
         | that in an _n_ -times nested expression (or nested, non-
         | recursive function calls), and for a list of length _k_ the
         | result is a list of length _kn_. And with _reduce_ you could
         | then concatenate a string with itself those _kn_ times,
         | resulting in a string (and likely runtime and memory usage) of
         | length 2^ _kn_.
         | 
         | If you want to limit the runtime, you need to apply a timeout.
        
         | Expurple wrote:
         | Idris is Turing complete [1] and has a very advanced and
         | expressive type system (dependent types) where type checking is
         | still guaranteed to halt.
         | 
         | That's because the language has a notion of `total` functions
         | [2], and only `total` functions can be used for computing type
         | signatures. These `total` functions must terminate in finite
         | time and not crash. AFAIK, they aren't Turing complete, but
         | they're still pretty expressive. `partial`, Turing complete
         | functions are still allowed at runtime (outside of type
         | signatures) [1].
         | 
         | If I understand correctly, running Idris in the type-checking
         | mode (`--check`) should give you what you want.
         | 
         | [1]: https://cs.stackexchange.com/a/23916/159425
         | 
         | [2]:
         | https://idris2.readthedocs.io/en/latest/tutorial/typesfuns.h...
        
       | samsquire wrote:
       | Thanks for sharing your thoughts.
       | 
       | I am also agreeing that relational approach to in-memory data is
       | a good, efffective thought.
       | 
       | I recently compiled some of my C code with the sqlite database
       | and I'm preparing to think how the SQL model of my standard code
       | could be used as the actual implementation language of in memory
       | operations.
       | 
       | Instead of writing the hundredth loop through objects I just
       | write a SQL query instead with joining with seeing the internal
       | data representation of the software as an information system
       | instead of bespoke code.
       | 
       | I was hoping to make it possible to handle batches of data and
       | add parallelism because arrays are useful when you want to
       | parallelise.
       | 
       | I was thinking, wouldn't it be good if you could write your SQL
       | queries in advance of the software and then parse them and then
       | compile them to C code (using an unrolled loop of the SQLite VM)
       | so they're performant. (For example, instead of a btree for a
       | regular system operation, you can just use a materialised array a
       | bit like a filesystem so you're not rejoining the same data all
       | the time)
       | 
       | I was thinking of ways of representing actors somehow
       | communicating by tables but I do not have anything concrete for
       | that.
        
         | mingodad wrote:
         | Using the same idea there is https://datadraw.sourceforge.net/
         | and https://github.com/google/rune using it.
         | 
         | DataDraw is an ultra-fast persistent database for high
         | performance programs written in C. It's so fast that many
         | programs keep all their data in a DataDraw database, even while
         | being manipulated in inner loops of compute intensive
         | applications. Unlike slow SQL databases, DataDraw databases are
         | compiled, and directly link into your C programs. DataDraw
         | databases are resident in memory, making data manipulation even
         | faster than if they were stored in native C data structures
         | (really). Further, they can automatically support infinite
         | undo/redo, greatly simplifying many applications.
        
         | refset wrote:
         | For anyone happy enough to consider dealing with the JVM
         | instead of C, and Clojure instead of SQL, I think this CINQ
         | project can deliver on much of what you're looking for here:
         | https://github.com/wotbrew/cinq
         | 
         | > I just write a SQL query instead with joining with seeing the
         | internal data representation of the software as an information
         | system instead of bespoke code
         | 
         | This sounds very similar to how CINQ's macro-based
         | implementation performs relational optimizations on top of
         | regular looking Clojure code (whilst sticking to using a single
         | language for everything).
        
       | tome wrote:
       | I'm surprised these are called " _programming language_ ideas ".
       | They seem to be solvable, at least many of them, with
       | _libraries_. For example, my Haskell effect system Bluefin can be
       | seen as a capability system for Haskell. My database library
       | Opaleye is basically a relational query language for Haskell.
       | Maybe I 'm short-sighted but I haven't seen the need for a whole
       | new _language_ to support any of that functionality. In fact one
       | gets huge benefits from implementing such things in an existing
       | language.
       | 
       | * https://hackage.haskell.org/package/bluefin
       | 
       | * https://hackage.haskell.org/package/opaleye
        
         | truculent wrote:
         | One advantage (which is touched on in the logging section) is
         | that having it provided by the language makes it clear what the
         | default is, and sets expectations. Essentially, lifting it into
         | the language is a way of coordinating the community.
        
       | vrighter wrote:
       | "Semi-Dynamic Language" - Zig?
       | 
       | "Value Database" - Mumps? lol
        
       | ninalanyon wrote:
       | The only thing I want added to every programming language I use
       | is the ability to call functions and handle data structures
       | provided by libraries and services written in other languages
       | without me having to write arcane wrappers.
        
       | dan00 wrote:
       | I think the problem with "big" language ideas is, that as long as
       | they match exactly your needs, they're great, but if they're
       | slightly off, they can be a pain in the ass.
       | 
       | I'm wondering if languages could provide some kind of meta
       | information, hooks or extension points, which could be used to
       | implement big ideas on top. These big ideas could then be reused
       | and modified depending on the needs of the project.
        
       | dexen wrote:
       | In which Jerf longs for PHP. Every single point has been in, and
       | actively used, for a long while. The __call() & friends is
       | particularly nifty - simple mental model, broad applicability, in
       | practice used sparingly to great effect.
       | 
       | All in all a very enjoyable post.
        
       | nobodywasishere wrote:
       | > What about a language where for any given bit of code, the
       | dynamicness is only a phase of compilation?
       | 
       | This is (essentially) Crystal lang's type system. You end up with
       | semantic analysis/compilation taking a significant amount of
       | time, longer than other comparable languages, and using a lot of
       | resources to do so.
        
       | movpasd wrote:
       | I think the coloured function problem boils down to the fact that
       | async functions are not naturally a specific kind of sync
       | function, but the other way around.
       | 
       | Functions are so ubiquitous we forget what they really are: a
       | type of guarantee about the conditions under which the code
       | within will run. Those guarantees include the availability of
       | arguments and a place to put the return value (on the stack).
       | 
       | One of the key guarantees about sync functions is the call
       | structure: one thread of execution will be in one function and
       | one function only at any point during the program; the function
       | will only be exited on return (or exception, or panic) or call of
       | another function; and all the local data will be available only
       | for the duration of that function call.
       | 
       | From that perspective, async functions are a _weakening_ of the
       | procedural paradigm where it is possible to "leave behind" an
       | instruction pointer and stack frame to be picked up again later.
       | The ability to suspend execution isn't an additional feature,
       | it's a missing guarantee: a generalisation.
       | 
       | There is always an interplay between expressiveness and
       | guarantees in programming languages. Sometimes, it is worth
       | removing a guarantee to create greater expressiveness. This is
       | just an example of that.
       | 
       | I mentioned exceptions earlier -- it's no wonder that exceptions
       | and async both get naturally modelled in the same way (be it with
       | monads or algebraic effects or whatever). They are both examples
       | of weakening of procedural guarantees. Exceptions weaken the
       | guarantee that control flow won't exit a function until it
       | returns.
       | 
       | I think the practical ramifications of this are that languages
       | that want async should be thinking about synchronous functions as
       | a special case of suspendable functions -- specifically the ones
       | that don't suspend.
       | 
       | As a counterpoint, I can imagine a lot of implementation
       | complexities. Hardware is geared towards the classical procedural
       | paradigm, which provides an implementation foundation for
       | synchronous procedures. The lack of that for async can partially
       | explain why language authors often don't provide a single async
       | runtime, but have this filled in by libraries (I'm thinking of
       | Rust and Kotlin here).
        
         | pjc50 wrote:
         | You can I believe emulate async with call-cc (call with current
         | continuation), but I'm not aware of work in that area and not a
         | lot of non-LISP languages support this kind of continuation.
        
       | Imustaskforhelp wrote:
       | I relate to this post so so much.
       | https://jerf.org/iri/post/2025/programming_language_ideas/#v...
       | 
       | To me , this idea seems so so insane (especially for things like
       | extraction , like you start extracting a zip on one device and it
       | can be partially extracted and then you can partially extract it
       | on the other) (yes sure , you could loop over each file and have
       | a list of files currently unzipped and rather unzip the file
       | which hasn't been unziped yet _)
       | 
       | But Imagine if the file to be extracted is a singular file in zip
       | (like 100 gig file)
       | 
       | I don't know , I have played this with criu and it had worked.
       | Qemu can also work. But this idea is cool
       | 
       | Instead of using a default storage where entropy can hit , I
       | would personally like it if the values were actually stored in
       | sqlite and combined with Truly Relational Language maybe as well
       | (but it doesn't truly require you to learn sqlite)
       | 
       | I had posted this on one of hackernews this as well and
       | theoretically its possible with the brainfu* in sqlite intepreter
       | that I had found. But I don't know.... If anybody knows of a new
       | language / a method for integrating this in new languages , it
       | would be pretty nice._
        
         | Imustaskforhelp wrote:
         | Oh my god , Another banger is the modular monolith part which I
         | personally believe that it can be considered that java / kotlin
         | ecosystem , golang with nats , elixir / erlang can be.
         | 
         | Another cool way is using encore in golang or typescript and
         | then hosting the aws stack yourself or running encore locally I
         | am not sure)
         | 
         | There is also sst framework which can allow to be run on docker
         | and also https://github.com/vercel/fun
        
       | crabbone wrote:
       | Several unrelated comments:
       | 
       | * In general, whenever I hear "compiler will optimize this", I
       | die a little on the inside. Not even because it's delegating
       | solution of the newly created problem to someone else, but
       | because it creates a disconnect between what the language tells
       | you is possible and what actually is possible. It encourages this
       | kind of multi-layer lie that, in anger, you will have to untangle
       | later, and will be cursing a lot, and will not like the language
       | one bit.
       | 
       | * Capabilities. Back in the days when ActionScript 3 was
       | relevant, there was a big problem of dynamic code sharing. Many
       | services tried to implement module systems in AS3, but the
       | security was not done well. To give you some examples: a gaming
       | portal written in AS3 wants to load games written by programmers
       | who aren't the portal programmers (and could be malicious, i.e.
       | trying to steal data from other programs, or cause them to
       | malfunction etc.) ActionScript (and by extension JavaScript 4)
       | had a concept of namespaces borrowed from XML (so not like in
       | C++), where availability of particular function was, beside other
       | things, governed by whether the caller is allowed to access the
       | namespace. There were some built-in namespaces, like "public",
       | "private", "protected" and "internal" that functioned similar to
       | Java's namesakes. But users were allowed to add any number of
       | custom namespaces. These namespaces could be then shared through
       | a function call in a public namespace. I.e. the caller would have
       | to call the function and supply some kind of a password, and if
       | password matched, the function would return the namespace object,
       | and then the caller could use that namespace object to call the
       | functions in that namespace. I tried to promote this concept in
       | Flex Framework for dealing with module loading, but that never
       | was seriously considered... Also, people universally hated XML
       | namespaces (similar to how people seem to universally hate
       | regular expressions). But, I still think that it could've
       | worked...
       | 
       | * All this talk about "dynamic languages"... I really don't like
       | it when someone creates a bogus category and then says something
       | very general about it. That whole section has no real value.
       | 
       | * A Truly Relation Language -- You mean, like Prolog? I wish more
       | relational databases exposed their content via Prolog(like)
       | language in addition to SQL. I believe it's doable, but very few
       | people seem to want it, and so it's not done.
        
       | wavemode wrote:
       | Monads can abstract over most of these things.
        
       | moth-fuzz wrote:
       | Regarding the 'Modular Monoliths' bit, I wholeheartedly agree. I
       | always found it kind of disappointing that while we're told in
       | our OOP classes that using interfaces increases modularity and
       | cohesion and decreases coupling, in reality in most programming
       | languages you're relying on the _nominal_ type of said interface
       | regardless. All libraries have to use a common interface at the
       | source code level, which is obscenely rare. For interfaces to
       | truly live up to what they 're describing, they merely ought to
       | be structural (or whatever the equivalent to functions is that
       | structural typing is to data).
       | 
       | Edit, since I remembered Go has this behaviour: I think Go's
       | auto-interfaces I think are easily one of its biggest selling
       | points.
        
       | c-smile wrote:
       | = Value Database
       | 
       | I've added this feature to QuickJS [1] and it works quite well in
       | Sciter as persistent Storage [2] mechanism, used in Sciter.Notes
       | [3] for example.                    let storage =
       | Storage.open(filename);          let persistentData =
       | storage.root;          if( !persistentData ) storage.root =
       | persistentData = {... initial storage structure ...};
       | 
       | Everything written to persistentData will be persistent between
       | runs.
       | 
       | = Semi-Dynamic Language
       | 
       | De-facto we already use similar approach quite a while. In form
       | of GPU shaders or WebAssembly. The solution is not in making
       | script JIT friendly (that is against its nature) but with an
       | option to use native/compiled/loadable modules written in
       | languages that were designed to be compileable from the
       | beginning.
       | 
       | My Sciter, as an embeddable engine, is an example of such
       | environment. Native host application exposes native
       | functions/classes to HTML/CSS/JS engine that implements UI layer
       | of the application. UI is dynamic (fluid,styleable,etc.) by
       | nature while application backend (a.k.a. business logic layer) is
       | more static and linear by nature.
       | 
       | [1] https://gitlab.com/c-smile/quickjspp
       | 
       | [2] https://docs.sciter.com/docs/Storage/introduction
       | 
       | [3] https://notes.sciter.com/
        
         | kridsdale1 wrote:
         | I wanted to comment on that persistent data point that on Apple
         | platforms, you'll have NSUserDefaults (objc) and
         | UserDefaults.myVar (Swift). It works just like the author
         | wishes. Other than single thread access, there's no major
         | problems with it. If you design your program in a way that it
         | falls apart if the use of it is not regulated, that's on you.
        
           | 9rx wrote:
           | _> on Apple platforms, you'll have NSUserDefaults (objc) and
           | UserDefaults.myVar (Swift). It works just like the author
           | wishes._
           | 
           | You could write something similar to NSUserDefaults in any
           | language for any computing environment, but that's not what
           | the author is talking about. He is talking about the same
           | basic concept, yes, but where it exists within in the
           | language itself, not something implemented on top.
        
           | c-smile wrote:
           | NSUserDefaults is a key-value storage. Same as localStorage
           | in browsers/js.
           | 
           | On other side persistence in QuickJS is more than that.
           | Essentially it is a NoSQL DB integrated into the language and
           | its runtime. For example you can write                  let
           | uname = root.users[2].firstName;
           | 
           | to access the data using pure language constructs. While with
           | NSUserDefaults you will need to call DB's facade methods like
           | objectForKey("some") and so on.
           | 
           | And also, in QuickJS, Storage is not reading whole DB in
           | memory but fetches/unloads data on demand transparently for
           | the user. You can think about content of DB as about genuine
           | language data structure with root at storage.root
        
       | perlgeek wrote:
       | I really wish more languages would "steal" grammars from raku
       | (formerly Perl 6).
       | 
       | A grammar is basically a class (or role/trait), and they can
       | contain regexes (and regular methods). Those regexes have
       | backtracking control (for simple tokens, you don't want to to try
       | parse a string any other way than the first, obvious match).
       | 
       | This makes it much easier to write composable and understandable
       | parsers.
       | 
       | I know that, technically, you could do that in a library, but
       | somehow that's never the same; if it's not baked into the
       | language, the hurdle to introduce another dependency is always
       | looming, and then if there's more than one such library, the
       | parsers aren't composable across libraries and so on.
        
       | teleforce wrote:
       | a) Capabilities:
       | 
       | Perhaps programming language is not the right abstraction to
       | implement capability and need strong support from hardware and
       | OS. OS with CPU based memory segmentation is an old idea probably
       | worth re-exploring.
       | 
       | Implementing capability in the programming languages constructs
       | will only increase cognitive overload and it will not be helpful
       | for the programmer productivity [1].
       | 
       | b) Semi-Dynamic Language:
       | 
       | Dynamic language is what we want but static language is what we
       | need [TM]. Instead of making dynamic language more static why not
       | make static language more dynamic?
       | 
       | I think D language is moving the right direction with the default
       | GC, RDMD based scriting, CTFE, and Variant based standard library
       | features [2].
       | 
       | c) A Truly Relational Language:
       | 
       | Relational is only but one of the techniques of data processing,
       | and other popular ones are spreadsheet, graph and matrices.
       | 
       | Rather than constraint programming languages with relational
       | native constructs, better to provide generic mechanisms with
       | fundamental constructs for data with associative array algebra
       | [2].
       | 
       | d) Value Database:
       | 
       | This very much relates with (c) and can be excellent by-products
       | and side-effects solution of it.
       | 
       | e) A Language To Encourage Modular Monoliths:
       | 
       | I think this is best point and idea from the entire article but
       | as it rightly pointed out this is mainly architecture problem and
       | programming language plays as a supporting role. It's the same as
       | the Internet is based on packet switching rather than circuit
       | switching, regardless of the RFCs and standards are being
       | implemented in any languages.
       | 
       | However, for OS architecture with regard to Linus vs Tanembaum
       | debate, modular monolithic is what we have now and the most
       | popular in the form Linux, Windows and some say MacOS, and
       | together they cover more than 99% of our OSes.
       | 
       | [1] Cognitive load is what matters:
       | 
       | https://news.ycombinator.com/item?id=42489645
       | 
       | [2] std.variant:
       | 
       | https://dlang.org/phobos/std_variant.html
       | 
       | [3] Mathematics of Big Data: Spreadsheets, Databases, Matrices,
       | and Graphs:
       | 
       | https://mitpress.mit.edu/9780262038393/mathematics-of-big-da...
        
       | pmarreck wrote:
       | roc-lang is pretty cool
        
       | janalsncm wrote:
       | One thing I would like in PyTorch is for a tensor's shape to be a
       | fundamental part of its type. That is, disable implicit
       | broadcasting and if an operation would require adding dimensions
       | to inputs, require those inputs to be type cast to the correct
       | shape first.
       | 
       | I can't tell you how much time I have wasted on broadcasting
       | bugs, where operations "work" but they aren't doing what I want
       | them to.
       | 
       | Jax can do this but no one uses Jax because of other reasons.
        
       ___________________________________________________________________
       (page generated 2025-01-09 23:01 UTC)