Hacker News new | past | comments | ask | show | jobs | submit login

A lot of innovative frameworks take the approach of forcing everything through a single paradigm, so that you can avoid solving a whole class of problems.

It seems like a good trade off—yes, everything must be done just so, and perhaps some parts of your app are a little awkward, or a little boilerplate-y, but there’s a whole giant class of problems you can just totally ignore.

Until... you can’t.

There’s another approach to library authorship, where the goal is not to delete a class of problems, but to make a class of problems easily controllable. Not to make a large domain disappear, but to make it programmable.

I have found, with that as your goal, you will be pushed away from declarative models. Because declarative models are about making a surface which is not programmable but configurable.

You can’t program arbitrary relationships across a React component boundary. You must flatten your intent into the very tight declarative interface which exists there.

Procedural models allow you to use the full set of capabilities that a function call and type system gives you. Ideally you don’t use much of that capability at any one time. But the beauty is you can use exactly the right control structure for the specific concern you are trying to... not abstract away, but make controllable.

The set of “concerns” is the infinite set. And no possible declarative model can capture it. Functions can.

You just need to know where your escape hatch is.

   SQL -> SQL procedures

   Angular templates -> DOM manipulation

   Puppet -> custom scripts
And you may even find it depends on the context.

For example, HTML/SVG/CSS DOM scripting can be considered "procedural", but the in other sense the DOM is a rather declarative version of rendering, whose procedural escape hatch is Canvas/WebGL.

You may even find case where they are combined tightly:

A makefile has declarative dependencies and procedural recipes.


But once you are out the escape hatch you will need to model both the new space you are working in, AND model the world you escaped from in order to interoperate, which is a special hell.

This hell has a name you will recognise: the leaky abstraction.

The idea is always to put a layer on top that simplifies things, so it's easier to use and faster to learn. But the end result is usually you have to learn both the abstraction and thing it was trying to hide.

> And no possible declarative model can capture it. Functions can.

If functions are your declarative model, what would you say?

Well, in most apps you will layer both kinds of models. You’ll have SQL which is declarative, but you’ll call those queries as procedures. The boundaries around a React component are declarative, but the render function is procedural.

So when I talk about declarative models I’m talking about the Swiss cheese of declarative models with procedures mixed in.

In moxie, everything is procedural, as it were. The declarative syntax is a way to invoke "imperative but idempotent" functions, and the core runtime provides tools for wrangling mutability and repetition in those functions.

React is just a very sophisticated template engine. It's something the server side rendering model used for decades.

I don't need the freedom to invent my own character encoding, or the freedom to create SQL injection vulnerabilities, buffer overflows or use after free bugs. That's just something that drags me down when I'm writing a nice web app to have fun with friends or a good old enterprisey web app at work. I need the freedom to create working applications quickly.

There are procedural APIs to do everything you mentioned.

I agree. In theory, declarative languages let programs reason about the content more easily. But I can't think of when that has actually been a real benefit.

Flutter gets it right. The code looks almost like it is declarative. But really it's just Dart code (an underappreciated language IMO) and you never get stuck by things that are impossible to express.

> The code looks almost like it is declarative. But really it's just Dart code

I need to figure out how to express this better in the future, but if you swap "Dart" for "Rust," this sentence applies to moxie as well. It's what I mean when I say that the callgraph of functions is used for structure.

Yeah, the way I would say this is that "being declarative" is the goal, and the broad strokes of the way you express app logic (one way data binding and so on) are pretty similar, but the implementation details of how you get there vary widely. Expressing the app logic as just running some code, with the side effect of generating your UI, is an appealing approach, because it means that all of the power of your host language is under your fingertips if you need it.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact