Yes, very well said. Computability cannot exceed that of a Turing machine, and the real power is in composability: encapsulating pieces of software into composable chunks ("modules") allows for reuse & powerful expression through abstraction. One could even argue that that's what programming is.
My goal is to extend this composibility into programming language, into the runtime, and ultimately right out into the end-user's hands; but I'm not trying to change computation by inventing a "more-Turing"machine.
Composable Language:
One who crafts programs ought to be able to employ whatever execution mechanisms / model he sees fit; that is, if he is really free to craft whatever he wants. Using a high level "programming language" is allowing those decisions to be made for you. I think a different paradigm of "language" is possible, where one composes it from both custom and 3rd-party pieces. This is similar to using a library (a collection of tools that can you use in your program), whereas using a static language is like using a framework (the backbone of a pre-made program that you customize via fill-in-the-blank).
Some argue that language composition is already available through code transformations (macros, A -> B compilers, runtime code generation, etc.); but that's not the same thing, because the resulting program is still operating in terms of mechanisms provided in the target language. For example, Java's "lambdas" generate full-fledged classes; and good-luck implementing closures in Java without nasty overhead tricks! Hey, if a coroutine or a generator or continuation is a perfect fit for the task at hand, and I (or somebody else) already knows how to implement it, then I should be able to use it in my programs.
Tools like JetBrains MPS are a step in the right direction, but don't solve the larger problem of runtime composition.
Composable Runtime:
Source code is not software. Source code specifies software to be generated. Editing source code creates results a new specification from when a new software artifact can be generated. What I'm getting at is being able to manipulate & compose the actual software artifacts themselves: You can have all the coding tools in the world that let you manipulate & transform your code however you like, and you still end up with a locked-down as-is artifact. However, if software itself were made to be directly manipulated and composable, then one could own his software as a USER (in the same manner that I talked about owning the design of a program as a crafter).
This requires that software artifacts be composed in a manner that they can be editable, which means building blocks that can be created / inspected / manipulated at runtime ... and we essentially end up with something like LISP or JavaScript, but with an interface to runtime software entities. And if this runtime is to be truly ownable as well, then it must apply to itself (hence title of this forum topic).
At that point, one can manipulate software entities without writing code. This becomes more true as one makes tools to assist with common tasks. The repetition of this process results in an environment where one can use user-interfaces instead of code, and where "programming language" can be less of a defined whole and more of an evolving collection of software-manipulating tools. (DSLs can be implemented as functions which take entities as their "code" and either generate new code, or interpret it directly). Furthermore, there's no reason why a software entity couldn't contain it's own engine & DSL-based code. A similar (though different) thing happens all the time when webpages dynamically load other libraries as needed.
<The below material is older and needs review / editing>
For example, one could hypothetically edit an IDE in the IDE itself ... It rather, one could edit the SOURCE for it, and that's not the same thing, because then you generate some new artifact and call it a new version of the other one generated. What I'm talking about is that you edit the IDE code in the IDE, and you IMMEDIATELY have the feature change available for use. Hypothetically, one could repeat this kind of cycle every few seconds to "sculpt" the product as desired, improving the tools fit doing so as the need becomes apparent. For that to work, you NEED a flexible runtime where the "code" is not compiled away to something else.
Also, it would sick to have all that power within a runtime, but then to have to go back to the old way of doing things to update the runtime itself; especially if that runtime is to "become" the computer. Since you cannot get away from a static base layer, a possible solution is to wrap those pieces (which need only be few and small) in the same constructs that it operates on, so that it is at least possible to edit the runtime from itself, even if it's the equivalent and viewing and modifying "assembly" code (using that term loosely) inline. This either requires that the whole thing stay running and have its definition be tied to what's in memory, or to have some hook to save it off ... However it's done doesn't matter, so long as it then can be changed if needed later without having to stop the world and recreate it again ("recompile"), or at least if the runtime state can be saved and resumed.
Also, you've really got to look at Brett Victor's examples to get an idea of what this might look like as a user tool rather than just as a programmer tool. He talks about a tool for dynamic images, but that does not escape that "programmer had to invent the world for you" situation unless the tool can be applied to itself, which cannot exist unless the artifact (not it's blueprint) is directly modifiable, which I think means that the artifact is it's own blueprint.
... Is that making a clearer picture? Without that runtime changeability, I think what I'm talking about is not much different than LISP. Though I could still talk about replacing a MOP with a self-MMOP. (but that's CLOS, which is implemented IN LISP, and therefore somewhat irrelevant anyway)
My goal is to extend this composibility into programming language, into the runtime, and ultimately right out into the end-user's hands; but I'm not trying to change computation by inventing a "more-Turing"machine.
Composable Language:
One who crafts programs ought to be able to employ whatever execution mechanisms / model he sees fit; that is, if he is really free to craft whatever he wants. Using a high level "programming language" is allowing those decisions to be made for you. I think a different paradigm of "language" is possible, where one composes it from both custom and 3rd-party pieces. This is similar to using a library (a collection of tools that can you use in your program), whereas using a static language is like using a framework (the backbone of a pre-made program that you customize via fill-in-the-blank).
Some argue that language composition is already available through code transformations (macros, A -> B compilers, runtime code generation, etc.); but that's not the same thing, because the resulting program is still operating in terms of mechanisms provided in the target language. For example, Java's "lambdas" generate full-fledged classes; and good-luck implementing closures in Java without nasty overhead tricks! Hey, if a coroutine or a generator or continuation is a perfect fit for the task at hand, and I (or somebody else) already knows how to implement it, then I should be able to use it in my programs.
Tools like JetBrains MPS are a step in the right direction, but don't solve the larger problem of runtime composition.
Composable Runtime:
Source code is not software. Source code specifies software to be generated. Editing source code creates results a new specification from when a new software artifact can be generated. What I'm getting at is being able to manipulate & compose the actual software artifacts themselves: You can have all the coding tools in the world that let you manipulate & transform your code however you like, and you still end up with a locked-down as-is artifact. However, if software itself were made to be directly manipulated and composable, then one could own his software as a USER (in the same manner that I talked about owning the design of a program as a crafter).
This requires that software artifacts be composed in a manner that they can be editable, which means building blocks that can be created / inspected / manipulated at runtime ... and we essentially end up with something like LISP or JavaScript, but with an interface to runtime software entities. And if this runtime is to be truly ownable as well, then it must apply to itself (hence title of this forum topic).
At that point, one can manipulate software entities without writing code. This becomes more true as one makes tools to assist with common tasks. The repetition of this process results in an environment where one can use user-interfaces instead of code, and where "programming language" can be less of a defined whole and more of an evolving collection of software-manipulating tools. (DSLs can be implemented as functions which take entities as their "code" and either generate new code, or interpret it directly). Furthermore, there's no reason why a software entity couldn't contain it's own engine & DSL-based code. A similar (though different) thing happens all the time when webpages dynamically load other libraries as needed.
<The below material is older and needs review / editing>
For example, one could hypothetically edit an IDE in the IDE itself ... It rather, one could edit the SOURCE for it, and that's not the same thing, because then you generate some new artifact and call it a new version of the other one generated. What I'm talking about is that you edit the IDE code in the IDE, and you IMMEDIATELY have the feature change available for use. Hypothetically, one could repeat this kind of cycle every few seconds to "sculpt" the product as desired, improving the tools fit doing so as the need becomes apparent. For that to work, you NEED a flexible runtime where the "code" is not compiled away to something else.
Also, it would sick to have all that power within a runtime, but then to have to go back to the old way of doing things to update the runtime itself; especially if that runtime is to "become" the computer. Since you cannot get away from a static base layer, a possible solution is to wrap those pieces (which need only be few and small) in the same constructs that it operates on, so that it is at least possible to edit the runtime from itself, even if it's the equivalent and viewing and modifying "assembly" code (using that term loosely) inline. This either requires that the whole thing stay running and have its definition be tied to what's in memory, or to have some hook to save it off ... However it's done doesn't matter, so long as it then can be changed if needed later without having to stop the world and recreate it again ("recompile"), or at least if the runtime state can be saved and resumed.
Also, you've really got to look at Brett Victor's examples to get an idea of what this might look like as a user tool rather than just as a programmer tool. He talks about a tool for dynamic images, but that does not escape that "programmer had to invent the world for you" situation unless the tool can be applied to itself, which cannot exist unless the artifact (not it's blueprint) is directly modifiable, which I think means that the artifact is it's own blueprint.
... Is that making a clearer picture? Without that runtime changeability, I think what I'm talking about is not much different than LISP. Though I could still talk about replacing a MOP with a self-MMOP. (but that's CLOS, which is implemented IN LISP, and therefore somewhat irrelevant anyway)