6

A few programming language features I’d like to see – Neil Madden

 1 year ago
source link: https://neilmadden.blog/2023/01/18/a-few-programming-language-features-id-like-to-see/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

A few programming language features I’d like to see

I enjoyed Hillel Wayne’s recent newsletter about microfeatures they’d like to see in programming languages. A “microfeature” is essentially a small convenience that makes programming in that language a bit easier without fundamentally changing it. I love this idea. I’m partial to a bit of syntactic sugar, even if it can cause cancer of the semicolon. I have a few features that I’d love to see more languages adopt, so I thought I’d join in the fun. Unlike Hillel’s, however, most of mine are much larger changes. I’ve long thought it was time for a bit more experimentalism to return to programming language design, and not just for type systems! Maybe these ideas will inspire you to think up some whacky new programming languages. Let me know in the comments.

E’s quasi-literal syntax

The E language is a veritable cornucopia of interesting programming language ideas. But to pick one that I really like it’s the quasi-literal syntax for safely constructing values in other languages: SQL, HTML, etc. There were several iterations of this idea in E, and the documented versions are older I believe. But the basic idea is that you have a generic syntax for constructing multi-line strings with embedded expressions. On the surface this is a familiar idea from many languages, but E has a nice twist on it. For example, given a code snippet like the following

def widgetName := “flibble”
def widgetCount := 42
def sql := ```
INSERT INTO widgets(name, count)
VALUES($widgetName, ${widgetCount + 1})
```
db.exec(sql)

what gets constructed here is not a simple string, but rather some kind of Template object/record. That template object has two fields:

  • A list of fragments of the literal string before and after each variable reference: “INSERT INTO … VALUES(”, “, ”, and “)” in this case.
  • A list of the (evaluated) expression values. In this case, that is the value of the widgetName variable and the result of adding 1 to widgetCount.

The database exec() method then doesn’t take a String, but rather one of these Template objects and it applies appropriate processing based on the contents. In this case, by constructing a prepared statement, something like the following:

def exec(template) {
def sql = template.fragments.join(“?”)
def stmt = db.prepare(sql)
stmt.bind(template.values)
stmt.execute()
}

Here the (completely made up) code replaces the originally expressions with SQL placeholders in a prepared statement: “INSERT INTO … VALUES(?, ?)”. It then binds those placeholders to the actual values of the expressions passed in the template. (The exact type of the “values” field is questionable: E was dynamically-typed. For now, let’s assume that all values get converted to strings, so “values” is a list of strings: in this example “flibble” and “43”).

This has the effect of allowing the easy use of prepared statements, while having a syntax that is as easy to use as string concatenation. Safety and convenience. The same syntax can be used to provide safe templating when outputting HTML, XML, JSON, whatever.

(E’s actual approach was somewhat different to how I’ve presented it, but I think this version is simpler to understand. E also allowed the same syntax to be used for parsing too, but I think that is less compelling and complicates the feature).

Edit: /u/kn4rf and /u/holo3146 on Reddit point out that this already exists in JavaScript in the form of tagged template literals, and has been proposed for Java too in JEP-430. Very cool!

Datalog/Prolog as a sub-language

I’ve always loved Prolog and its cleaner cousin Datalog, but like many people, I find them hard languages to write a full application in. Logic programming is great for writing, well… logic. But it’s less good at the messy stuff. Robert Kowalski famously described the design of Prolog using the equation “Algorithm = Logic + Control”: the idea that the logical form of an algorithm can be separated from how it is executed (the control flow). In my opinion, we can go further and separate all the messy stuff around the edges. I propose the following equation:

Program = Logic + Control + Interaction

“Interaction” here covers a multitude of sins: user interfaces, networking, file I/O, operating system calls, etc. (Aside: parallelism is a control strategy, concurrency is interaction – hope that’s cleared that up!)

I would like to program the logic of my program in a pure logic programming language. (With a bit of sugar for functions, which are after all just a certain type of relation). And then I’d like to separately be able to specify how that logic should be executed: top-down vs bottom-up, sequential or parallel, etc. And I’d like to be able to call into this pure declarative logic from an imperative shell that handles all the messy stuff. Is that too much to ask?

Teleo-Reactive Programs

While we’re talking about the messy stuff of interaction, how about we do something a bit better than simple procedures or Java-style methods? When I was doing a PhD in AI (before deep learning took over the world), I was very taken with Nilsson’s Teleo-Reactive Programs (TRP). This was an approach to programming robots and other systems that is goal-driven but also reacts to unexpected changes in the environment. It doesn’t just keep blindly following a fixed procedure.

The basic idea is that you define a method to achieve some goal as an ordered list of conditions and corresponding actions, like the following contrived example:

to make_tea:
when perfect(tea) -> done
when brewed(tea) -> remove_teabag; add_milk
when hot(water) -> pour_into(cup); add_teabag
when cold(water) and full(kettle) -> boil_kettle
when empty(kettle) -> fill(kettle)

Although this looks like a simple set of if-then statements, the execution is quite different. When you invoke the make_tea method it starts a persistent process that continues until the tea is made (or an unrecoverable error occurs). At each cycle it scans through the list of conditions and executes the first one it finds that isn’t satisfied. Actions further down the list attempt to satisfy the preconditions of actions higher up the list. In this way, it is always trying to move closer to its goal condition (perfect tea).

However, the approach has some advantages. If some earlier task was completed but is somehow undone by another action — say, someone comes along and makes coffee with our freshly boiled water—the TRP will notice this the next time it scans through the list and so will automatically boil some fresh water. On the other hand, if a change in the environment serendipitously causes a higher-up goal to be satisfied already (someone puts the teabag in for us), the system will also notice that and automatically avoid duplicate work.

Although many of the examples for TRPs involve robotics and real-world tasks, this kind of intelligent goal-directed execution is incredibly useful in everyday programming too. If you’ve ever had to code retry and recovery logic with back-offs and abort conditions, you may see the value of something like TRP. Kubernetes has some similar ideas in its approach to declarative config: you describe your desired goal state for the system, and it performs a continuous diff between the current and desired states and corrects any differences it sees.

Design by Contract…

This is a simple one. I’d like to be able to easily annotate methods with pre- and post-conditions and have them automatically checked and documented:

to make_tea:
achieves full_of(cup, tea)
requires not empty(tea_box)

… with a STRIPS planner?

Once I’ve annotated all my methods with pre- and post-conditions, maybe I shouldn’t even have to manually bother calling them? What if I could just write:

achieve full_of(cup, tea)

and the language runtime went off and found some sequence of method calls that would achieve that post-condition.

Combine that with pre- and post-conditions specified in Datalog and methods implemented in TRPs and this starts to sounds kinda interesting! Of course, it might be terrible and I’m sure debugging would be a nightmare, but wouldn’t it be fun to at least try programming in a language like this?

Author: Neil Madden

Founder of Illuminated Security, providing application security and cryptography training courses. Previously Security Architect at ForgeRock. Experienced software engineer with a PhD in computer science. Interested in application security, applied cryptography, logic programming and intelligent agents. View all posts by Neil Madden


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK