Another Quora answer I should be moving here : Phil Jones’s answer to How would you design the perfect programming language?
Well, the obvious way you’d approach it, is start with a reasonable knowledge of using other languages to get a sense for their strengths and weaknesses.
And start with some good role models.
And then see where you can go by taking a good language and incorporating ideas from elsewhere.
It’s no secret that I think that Clojure is the nicest language I’ve ever used.
Clojure’s virtue is that
a) it’s built on very firm, 50 year old foundations, namely Lisp.
b) it was willing to do a clean slate reinvention, throwing out the cruft that had built up in other Lisps and Schemes. And bringing in good ideas from elsewhere.
I think that’s how you make a great language. Start with an already good one. And improve it with housekeeping and tasteful borrowings from elsewhere. (Note the “tasteful” … C++ took a good idea “object orientation” and kludged it into a good language, C. And somehow came up with a worse language. (Phil Jones’s answer to Why is C++ considered a bad language? )
That selectivity. Being willing to throw out bits that either got crufty or just didn’t fit the vision (eg. reader macros in Clojure) is as crucial a part of making a nice language as choosing good features to add.
So how would a great language seem to me today?
As I said here, languages need to express
- a) computation (ie. functions and compositions of functions)
- b) constraints
- c) data-structures
- d) “architecture”
From a language design point of view, I think “computation” is a solved problem. I think it’s great to do computation with function application and composition. I’m pretty happy with the way Lisp represents that. But I understand people who might prefer a different syntactic sugar for expressing functions like the ML / Haskell families. Or even if you want to start with Smalltalk’s objects passing messages, the “concatenative” approach of Forth, Erlang’s actors, or Prolog’s relations, then you might get somewhere interesting.
I’ll skip constraints (largely types and contracts) for the moment. And focus more on c) and d). A good language must have good ways of expressing complex data structures. This starts with a good way to express literals. Eg. strings, arrays, dictionaries etc. But moves on to expressing more complex data-schema. Perhaps grammatically.
I like Clojure’s EDN, which is like JSON, but having it really built into the language is very powerful. But you can go further with grammatical descriptions of data. Things like Clojure’s Spec. Or Shan’s “sequence calculus”. Or Haskell’s algebraic data-types etc.
Ironically, “object oriented” languages have been rather poor at expressing complex data. The most egregious ugliness of the C++ / Java type OO is due to just how painful it is to construct complexes of objects. You end up having to procedurally call operations on classes and objects saying “create a new instance of an X, now attach a new instance of a Y to it. Now attach to the Y, a reference back the Z from this scope”
It’s that mismatch between a program that thinks of itself as a network of interacting objects, and a language that has no explicit concept of “a network of interacting objects” and no convenient way to express it, that makes Java and friends such hard work. Somewhere deep in the heart of the UML is a good intuition, struggling to find a viable implementation.
That’s what I call “architecture”. The structure of the program itself. But architecture is also the shape of the UI. And the bindings between events and handlers. Or the representation of the overall network of different actors … the client, the server, the database etc.
A language which is good at defining data-structures could be good for defining all these architectural elements if you just added a few more bells and whistles to it. (See Phil Jones’s answer to Which is the best programming language for developing a GUI application? )
I’m fascinated by Jonathan Edwards’ concept of “social data-types”, ie. data-structures with built in declarative rules for how they should be synced and distributed and accessed across a networked application. This seems to me to be exactly the kind of thing we need in new languages : the number of useful algorithms to define how data is moved and synced and controlled around a network is probably small. They are fiddly to implement, but 99.999% of applications just need to state which they use.
For example, it should be possible to say stuff like “this is a thing called a Profile page. It has these fields. Each Profile page belongs to one SystemUser. And each SystemUser will have one ProfilePage. Only the SystemUser who owns the ProfilePage should be able to edit it. But all SystemUsers should be able to see / read it. Changes on the owners client will be automatically synced to the server, and changes on the server will be automatically synced to any non-owner’s client which is looking at the page”.
Don’t get hung up in the English. I’m not asking or interested in this being natural or “English-like” language. I’m saying it ought to be possible to declare these facts simply within the declaration of the data schema, and have the language’s own “synchronization engine” automatically do it.)
Now, of course, languages as diverse as HTML, SQL and Prolog are declarations of stuff with some engines behind the scenes to do the work (inference engines in Prolog, query engines in SQL, layout engines in Prolog)
But each individual engine is limited. Ideally we want to be able to have multiple engines for multiple purposes.
What I’m feeling towards, then, is
- a language which is great for defining data-structures and architecture (both general schemas and particulars)
- a language which is good for defining “engines”. I’m not sure I mean “implementing” engines. Perhaps they need to be implemented in some lower level language. But it should be possible in our high level language to say “here are some rules for something called “synchronization” which will be represented in this way within the program, will be handled by that engine, with these constraints or pre/post conditions”
The nice thing about rules is that they are easy to compose and have what Quildreen Motta seems to be calling “extensibility”. You can add or remove rules without worrying about how to slot them into the order of execution. You expect the engine to worry about that, and how to compose these types of rules together meaningfully.
A final point.
Yes, a good interactive mode of development inside a good IDE / REPL is crucial.
But I’ve been thinking for a while if it’s possible to go further.
Programming is still very bottom up. Even if we have a top-down view of our system, when we know it consists of modules X, Y and Z and they have to interact in this way. our development tools don’t really capture this information until we program it at the lowest level.
We have a bunch of tools that help us work with and reason about the higher level. We can write interfaces and type information and protocols etc. But until we instantiate them the language doesn’t really want to know.
We have unit tests. Which we ought to write first in TDD. In something like Clojure Spec, though, we can actually generate unit-test examples from our Spec.
That’s quite good but it’s a side trick.
What I think is one of the big pains of programming is that the computer demands we address problems in an order which suits it.
But imagine if we broke that restriction. Imagine if we could sit down with the IDE and have a “conversation” (remember I’m still not talking about English / natural language, just the ordering) like this :
Me : I’m going to build a system called “MyAPP” with a client and a server.
Computer : I got that, I know roughly what a client and a server are. And I’m storing our work under the name MyApp.
Me : Actually the client needs to run in both browser and on mobiles.
Computer : sure
Me : here’s the data-structure for the User and the UserProfile. And there’s a one-to-one relationship between them.
Computer : OK. Gotcha
Me : and the API basically consists of these commands. Oh, and to get started, here’s a state-machine I’m declaring for one of the interactions.
Computer : Understood. I’ve stubbed out the API and that protocol for you. Navigate here in your browser and try (with the default forms) some interactions based on that state-machine.
Me : thanks. Now I look at them, those forms are good, but I need to use a larger text box for the user description. And the classification_code is one of [:member, :editor, :administrator]
Computer : sure, I’m guessing that’s a drop-down in the UI, but how are you representing it in the UserRecord?
Me : On larger (browser pages) I prefer Radio Buttons, but on smaller pages and the mobile apps, yes a drop-down. In the data model I’ll go with it having to be one of these symbols, you can add that information to the schema of the UserRecord
Computer : already done.
Me : So now, this bit of the API defines the Admin controls. These should be limited to users who have the :admin classification_code.
Computer : Yep.
Me : Now, on that Dashboard page, I want you to add a chart that subscribes to a value called AllUserStats.flurpsPerMonth. It’s going to be on the form of integers against months.
Computer : OK. So I’m showing you some graphs with some random numbers against month data I just made up. Does that look OK? And how do you really really want to calculate flurps?
Me : it looks fine for the moment. So flurps are calculated from woobles. I know you don’t know anything about woobles yet, But woobles are floating point numbers between -2 and +2, and flurps come out of left-folding all the wooble information for each User’s month like this.
Computer : OK. I did that. You now have a flurp_calc transducer that’s hooked on to a stream that subscribes to User.woobleData (I don’t know what that is yet.) And the Flurps Per Month Graph on the admin page is now subscribed to that. I’m still making random shit up in User.woobleData, but at least those numbers are between -2 and +2. If flurp_calc is still wrong, that’s your fault.
Me : No probs. You’re doing good work computer. I gotta rush, but I’ll back tomorrow to pick up where we left off.
Now … just to re-iterate. This is NOT about natural language or Artificial Intelligence that means we can just talk to the computers.
I expect the above dialogue to happen in completely formal unambiguous statements using a sensible and efficient syntax.
What it’s about though, is a development environment that lets me, the programmer, add the decisions / constraints that define the system in the order that’s convenient for me. Behind the scenes there’s a model of the application. And that model is always kept in a state which can be run and tested, as a prototype / wireframe / stubbed or mocked up. It may break because I’ve put a bug into it. But if there is no bug then it’s never not doing something because I haven’t got around to doing something else yet. I’m jumping backwards and forwards between Model, View and Controller. Giving advice on fundamental algorithms, on architecture, on UI tweaks. I don’t need to build my application from the bottom up. I have a working application from the start, albeit one that does nothing. And I just keep adding extra information to refine and incrementally extend that application. In whatever order the ideas come to me.
Finally, I want to emphasize, this is not meant to be a description of a “web-app builder” tool. Knowledge of clients, servers, mobile-devices, web-applications etc. are in the pluggable engines. All the language knows about is :
- declaring data-structures and architecture
- declaring rules (how they look, and how they interface with an engine)
- describing the small amount of extra computation my system needs to do, probably in the form of functions
Beyond that, the development environment needs to know how to add new data-structures / functions and rules in the form of a dialogue, while keeping an up-to-date, coherent model of a system which is automatically generating a working prototype of that system.
Plug the Social Apps Framework (including engine) into the language / IDE at that point, and you now have the ability to grow your system via a “conversation” like the one above.
OK. Final repetition. The “perfect language” (or at least next leap forward) language I’m dreaming of is :
- a language with great expressivity for defining data-structures and rules
- a language where we expect to find most of the application (or “business”) logic written in the form of declarations of data-structures and rules which are handled by pluggable engines.
- a language where it’s easy to add and remove engines. Ideally perhaps the language can help us write the engines too.
- a language whose development environment affords a “conversational” way of building up a program by adding extra rules / constraints to an always live system
- I personally would start with Clojure … which already has a good Lispish syntax for functions and macros, and, in EDN, the beginnings of an OK data-structure language. But if you prefer to start from Smalltalk or Forth or Haskell or Prolog and do the Rich Hickey trick of a clean-slate refresh, keeping the good ideas, tastefully borrowing other good ideas, and removing the cruft, then that’s good too.