Give it enough time, every declarative language becomes a programming language. This is happening with all config files, markup languages, data formats.
The distinction between code, config and data is being erased. Everything is a soup now. Data is application, configuration is code. Code is an intermediate, volatile thing that is generated on the fly and executed in the temporary lambda containers.
> every declarative language becomes a programming language.
Overly pessimistic, lots of non-programming languages remain non-programming languages. Just because one of the most widely used declarative languages start adding conditionals doesn't mean the whole world is turning upside down...
> The distinction between code, config and data is being erased.
As as lisp programmer, I love it. Get rid of treating things differently, make everything the same and make everything work with everything, code should just be data.
> Get rid of treating things differently, make everything the same and make everything work with everything, code should just be data.
"Code should just be data" doesn't imply the converse, though; there's arguably utility in having data that isn't code, even with the premise that code should be data.
Or they are performant now. But once people start writing CSS code the same way the write JS code, they stop to be. You can still write super-tight code in ASM (or eve C) and it will be blazing fast. Almost nobody does it, because it's too hard. Once people start writing CSS the same way, it'll become slow and bloated too.
I am not a CS expert, but this does not look like a full implementation of rule 110, nor is it even pure CSS (there is HTML involved).
What I see in the SO answer is an interface for Rule 110 with an additional set of instruction (written in a natural language) for the user to execute manually. So you can use CSS + HTML to create an interface for a Rule 110, which is then written in a natural language around that interface. The answer even states that (very relevant) caveat.
> [...] so long as you consider an appropriate accompanying HTML file and user interactions to be part of the “execution” of CSS.
> The formal definition (simplest) of Turing Machine is simply a tuple of states set, symbol set, initial state, accepting states set and a transition function. There is no crank in it. By computation we mean somebody needs to apply the transition function faithfully on the tape which is exactly like the clicking in this case. More formally, a model of computation can be viewed as a set of rules somebody needs to follow to do the computation. In that sense, I think CSS is Turing-Complete.
The web should always have been a programming language, with all the usual constructs available in both the display and markup layers.
But instead of a single unified standard library for the industry we got a sprawling, ludicrous mess of multiple poorly thought-out semi-compatible technologies, with an associated sub-industry of half-baked fixes and add-ons.
As always, hindsight is 20/20, but when you're living it, the half-baked decisions and add-ons are a product of you figuring it out on the fly. You don't have the knowledge of which proposal will solidify into an industry standard, and you don't know which vestigial implementations will be a nightmare for backwards compatibility down the line.
The context is also lost. Javascript was famously coded in a day or whatever and called 'javascript' not ecmascript as marketing to compete with Java. Besides that well known case there's presumably thousands of esoteric business decisions made back then which shaped the "sprawling, ludicrous" landscape, and which are now lost to time.
Yes, the web should have always been a programming language. And the flying cars of 23xx should have never used a z-debuffer doodad.
> with all the usual constructs available in both the display and markup layers.
I'm glad the transition to mobile web accelerated on more battery efficient GPUs was possible due to the model instead of Alan Kay's idea that websites should render themselves, where each website would have needed to be upgraded for GPU support for compositing.
Because modern UI toolkits like Flutter proved that UI should just be code, not separated into three different languages. In this case, adding conditionals can remove the need for js in some cases, which is good.
So instead of having a css for x which defines e.g. dark mode and light mode separately, you can now define it via a single css rule.
Where previously the "tree" forked at the beginning and attributes were set multiple times, depending on various criteria - now you set it once, and evaluate it's value depending on criteria
If only Lisp had better presense in modern code editors. Emacs is not enough, especially on Windows where it is super slow. I think this is what actually stops newcomers to start with Lisp and not Python
> I think this is what actually stops newcomers to start with Lisp and not Python
What stops newcomers is knee-jerk reactions about the (lack of) syntax, it's scary to see something that doesn't look like Algol, because everyone who does mainstream programming uses Algol-like languages.
Introduce lisp to anyone who knows programming since earlier, and 99% of them will have a adverse reaction to s-expressions, before they understand what's going on. Once they understand, it makes a lot of sense obviously, but not everyone even has that kind of open mindset where they could understand if they wanted to.
My crackpot theory is that what stops newcomers is how unergonomic "(" and ")" are to type on typical keyboards. If mainstream lisp dialects used square brackets instead, we'd all be programming in it!
> If mainstream lisp dialects used square brackets instead, we'd all be programming in it!
You've almost convinced/nerd-sniped me to write a(nother) new lisp where we'll be using brackets for forms and lists and no parenthesis in sight. It's a wild theory.
I've had way more issues with proper indentation in Python and YAML than I have with parenthesis in lisp. Meaningful whitespace is about the worst idea I've seen in a programming language.
I spent more time than I'm willing to on large Terraform projects. How exactly is this relevant to declarative vs imperative or even my comment at all? I don't see what the "gotcha" is supposed to be here.
You make bad comments because I can't understand the point you're trying to make. I'm am engineer, I make choices based on informed tradeoffs, anything else would be sub-standard. Not sure why you think I only consider advantages, but I'm afraid asking you for clarification will just lead to more ramblings.
This is so true, I have seen it happen with so many projects. It always starts with a cute declarative DSL, and inevitably imperative / flow control structures emerge, at which point you wonder why they didn't use a real programming language in the first place and save you the hassle or learning a half baked imperative DSL.
- Puppet
- CMake
- Terraform
- ...
All these started with pure declarative DSL then incrementally created a nightmarish imperative monstrosity.
Ant did not include IF THEN ELSE, unless you added the contrib package.
If you understood the paradigm, you could write branches in Ant files simply using properties and guards on properties ("unless"). Using IF in Ant was basically admission of not having understood Ant.
This said, I used Ant for a very limited amount of time.
As of Ant 1.9.1, you can use 'if' and 'unless' attributes on any task or element in a target. I stopped using Ant a long time ago, but this was a pleasant discovery when I had to pick up an old Ant based project recently.
I agree, that is what I meant: there were people who installed Contrib to have <if> element, but in reality you did not need that you could just use Ant's built-in features like you said. In my opinion installing Contrib to use <if> was a demonstration of not having understood how Ant works.
For new .NET SDK style projects you hardly ever need to customize the defaults and I know it's used for more stuff than .NET, but I just wanted to give an example where it actually doesn't suck. Also, you may not need to do everything in MSBuild, for some more complex stuff, you can use something like Cake (https://cakebuild.net/) in .NET for example and skip the programming in XML.
.NET made into my toolbox before it was announced to the world, thanks to be working at a MSFT partner that was selected to be part of the Portuguese launch event for .NET with ready made products.
Never seen any big corp using alternative .NET build tools, rather wrestling MSBuild, or before it came to be, nmake.
You say that, but people in OCaml keep bemoaning the use of mostly declarative s-expressions in the Dune build system. Imagine the reaction if MSBuild used an actual Scheme.
"Modern CMake" is more about scoping all properties to the targets that they belong to (including stuff like what you also need to link against if you link against target foo) than about language features. The CMake language hasn't changed much except correcting some early weirdness about "if" and the addition of generator expressions, which are fortunately not often needed.
People love to hate on Maven's XML but at least it's been mostly the same since 2006. There are conditionals in profile activation expressions but they are very limited by design. Declarative done right, IMO
> All these started with pure declarative DSL then incrementally created a nightmarish imperative monstrosity.
"Huh?" I asked myself when you mentioned that Terraform is now imperative somehow. Took a look at the website again, and seems to still be HCL, and still be declarative. Am I missing something? How exactly is Terraform today a "imperative monstrosity"?
> Terraform has modules which are an elaborate method of doing function calls
... What? How is modules a function call? It's just a hierarchy, everything about/with modules is still declarative.
> HCL 2 has loops and conditionals. It is most definitely imperative.
So what? Just because there is loops and conditionals doesn't mean it's suddenly imperative.
How exactly you do loops in HCL? Last time I used it, you still used declarative configuration for that, and use `for_each` as an declared option, you don't "call for_each which returns config", all that happens inside of HCL/TF, because it is declarative.
Did something change like yesterday or are people even here on HN so ignorant about what declarative vs imperative actually means?
You "call" a module with arguments. You can call them multiple times. In every way that matters they are just like a function call.
I don't understand why there is a distinction between for each in a standard language vs for_each in HCL2. They are both do something by iterating over something else at runtime. The syntax isn't what matters here.
I think maybe you are mistaken in your own distinction between declarative and imperative.
Imperative: Tell the computer how to do something, by instructing it what to do.
Declarative: Tell the computer what you want the result to be like, and the computer figures out how to do it.
for_each in Terraform is very much declarative, just like modules. Compare how you'd do the same thing with JS or any other (imperative) language you know, and I think it must be clear what the difference between the two is.
Those boundaries are pretty fuzzy. The complexity of the logic with conditionals and loops in a module means that you have pretty much stopped describing what it should like and instead described how to make it look the way you want it.
I have read terraform modules where I had to execute the logic to know what got produced which moves it from your imperative description to the declarative description as far as I'm concerned.
You need unbounded recursion. Conditionals alone can’t do that. If you have some kind of conditional go to/jump if expression that’s a different matter.
You can emulate recursion with iteration and a push-down stack. If it doesn’t either recurse or offer both iterations (loops) and something that can act as a stack (at least an array or so) then it’s not Turing complete though. I have yet to see a stack or user-manipulable arrays in CSS.
Yup. It's the same reason nested CSS was added. It doesn't really add any new functionality. Just makes your CSS neater (or way messier when misused). it's syntactic sugar really
You can blame whoever invented the word "if", as soon as you can branch based on data you can just write an interpreter that turns data into instructions, no matter the architecture.
FWIW, you can make software that runs on Harvard architecture chips. They feature distinct address spaces for ROM and RAM. It's been a while, but it's how Atmel/Microchip AVR micros work.
Not really, most of these configuration as code systems are not executed directly on the CPU but rather interpreted in which case a separate data-only memory would not stop anyone.
I hope I get to see the next thing after browser applications in my lifetime. I fully understand the advantages, but it has grown so fast and so wild I think it has to eventually fall down by its own weight and complexity despite its immense success.
It's not that the presentation layer need conditionals, it's that the layers under it have grown full of hacks that need more hacks to work around them, because the web was designed to grow documents, not programs.
If the web had been designed for applications in the first place, the presentation layer probably wouldn't need conditionals at all.
It is a configuration language with general programming features, but it is decidedly _not_ Turing complete. It seems to sit at a sweet spot between "just JSON, no programming convenience at all" and "full-blown programming language with nontrivial toolchain".
It was shocking the first time I showed a lisp program to a (particularly "annoyed by everything") non-lisp developer who never apparently saw s-expressions before. Lots of knee-jerk reactions of "Oh my god so many parenthesis" and "How could anyone program like this?" while they sat there smug with their TypeScript codebase having more special characters, syntax and the same amount of parenthesizes, only because the opening parenthesis is one symbol to the left, instead of in the middle of the calls...
OP is not being very precise (and in a way that I don't think is helpful). There is nothing imperative in an if expression. Declarative languages can be Turing complete. Declarative languages are a subset of programming languages.
If you can mostly stick to the declarative way, it's still a benefit. No Turing-complete language completely prevents you from writing "bad" code. "You are not completely prevented from doing things that are hard to understand" is a bad argument. "You are encouraged to do things that are hard to understand" is a good one (looking at you, Perl).
It’s the cycle of newcomers to <field> looking at the existing solutions and declaring “this shit is too complicated, why did these morons design it this way? Check out my DSL that does everything and is super simple!”
Then time passes, edge cases start cropping up and hacks are bolted on to accommodate them. Eventually everything struggles under the weight of not having loops, conditionals, etc. and those are added.
> So why do people still design declarative languages?
Cost.
If money were no object, you would only hire people who can troubleshoot the entire stack, from React and SQL all the way down to machine code and using an oscilloscope to test network and power cabling.
Or put another way, it would be nice for the employer if your data analyst who knows SQL also knew C and how to compile Postgres from scratch, so they could fully debug why their query doesn’t do what they expect. But that’s a more expensive luxury.
Good software has declarative and imperative parts. It’s an eternal tradeoff whether you want the convenience of those parts being in the same codebase, which makes it easier to troubleshoot more of the stack, but that leads to hacks that break the separation. So sometimes you want a firm boundary, so people don’t do workarounds, and because then you can hire cheaper people who only need to know SQL or React or CSS or whatever, instead of all of them.
The distinction between code, config and data is being erased. Everything is a soup now. Data is application, configuration is code. Code is an intermediate, volatile thing that is generated on the fly and executed in the temporary lambda containers.