If you created your own programming language...

What would it look like? How would it work?

I’ve been thinking about this a little lately because of some annoyances I’ve run into in day to day programming. If I had my way, how would I make those go away? What would the programming language that best fits me (or you) look like?

1 Like

My language would be a direct refutation of [Gilad Bracha’s Mirrors paper][1]. I want reflection to be a core part of my own language’s syntax, semantics, and pragmatics.

This paper argues that meta-level functionality should be implemented separately from base-level functionality, using objects known as mirrors.

In a mirror-based system, one might write our example as:

ObjectMirror theCarsMirror = Reflection.reflect(myCar);
ClassMirror theCarsClassMirror = theCarsMirror.getClass();
ClassMirror theCarsSuperclassMirror = theCarsClassMirror.getSuperclass();

To me, the world of reflection described in that paper is a nightmare scenario, and ruins everything fun, useful, enjoyable, interesting, and good about reflection.

For people familiar with AS3, Gilad’s take is that people should be happy using flash.utils.getDefinitionByName instead of something like public::['String'] or public::[new QName('flash.display', 'Sprite')]

I think that the mirrors approach is disgusting, and makes me want to quit programming. (Much like much of Java in general.) Gilad wants reflection to be completely removed from the syntax of a language. I think it’s a fatal neutering of reflection. Relegating reflection to an obtuse API ruins the intuitiveness and convenience of using reflection, which are probably its best selling points.

Reflection is a usability (end-user programmer UI/UX) feature of programming languages, and putting it out to pasture in an obtuse barn is insulting.


In a similar vein, I think it’s a bad decision that arguments.caller and arguments.callee weren’t included in recent versions of ECMAScript. It’s understandable that people didn’t want those values on the arguments object, but I’d be in favor of adding the keywords callee and caller. Those wouldn’t be optimization killers, given how blindingly obvious they are even in the lexing phase of script execution.

I think it’s fairly cowardly of ES/JS implementors to shy away from features that made the language worth using. It’s annoying that people think that new is so entirely broken that people should use Object.create instead.

Object.getOwnPropertySymbols is a disappointing failure of ES6. There’s a two-part breakdown, where nobody wants to commit to syntax which disambiguates between ‘own’ properties, and nobody wants to commit to syntax for a difference between properties and symbols, even though symbols can have simple string identifiers. It reminds me of the Ruby :symbol GC issues, but it seems like they considered the problem and plowed forward with an awful solution. For a while, somebody on es-discuss really wanted so-called ‘private names’, but they gave that up to ES7 and we’re left with some weird amalgamation that might be useful to a few people. Private names made sense because there was an anti-forgery provision, versus Symbols, which can be forged from a string (IIRC).

Symbols are also interesting because accepted ECMAScript editions have traditionally eschewed namespaces (see ES4/AS3 for when that failed miserably), yet symbols are accepted within TC-39 and es-discuss as meta-properties. Meta-properties are at their core a namespacing mechanism, since they’re a space where you can store names without worrying as much about collisions. So there are dumb, standardized conventions for things like object iterators being named Symbol.iterator, where Symbol.iterator is a ““special value”” which you can’t replicate in JS code without typing/storing the Symbol.iterator value. But it’s kind of like saying that the Euro symbol in Unicode is a special value… it’s just a matter of what you let people type in their program code.


Things I like:

  • Ruby blocks
  • Swift autoclosures
  • Go channel syntax
  • Dynamic languages
  • Lambdas
  • LINQ
  • Beta’s inner
  • Go’s declared return values
  • Any language with implicit return values

Things I dislike:

  • Line-terminating semicolons
  • Whiny compilers for strictly-typed languages
  • Non-first-class functions, like Ruby methods
  • Signification indentation (Python, etc.)
  • Minimal syntax (Lisp, Scheme, etc.)
  • Statements over expressions. (foo = bar being a statement rather than an expression)

I could think of many more attributes of a language I might want to design/use, but this is good for a single session…
[1]: http://bracha.org/mirrors.pdf

1 Like

I knew I could count on you, krilnon :wink:

I’m still trying to gather up some of my own thoughts, and they’re probably already not going to be as comprehensive as you jotted down for this session. But on some of the things you said (many of which I’m not familiar with but will look into):

  • Reflection: Yes
  • Callee and Caller: Yes. In fact I’d almost categorize this into reflection in a sense, reflection of the call stack (not limited to just callee and caller)
  • Swift autoclosures: I had to look this one up and I don’t think I’m a fan. It looks like it hides the behavior of the argument in the function declaration which can make reading and understanding code confusing
  • Go: I only briefly looked into Go a couple of years ago, but it has some nice features such as with return values. Not too familiar with channels. I’ll have to look into that more
  • LINQ: I’ve always liked things like that. My pathetic attempts to keep E4X in AS4 were widely ignored.
  • I like carriage return meaning = yes, indention meaning = no, but its something I worry about not always working in all situations. I guess it really depends on the rest of your syntax and what can be inferred from that. Sometimes, and it feels dirty, I think enforcing consistency (as with indentation) can be a good thing too.

You’re right that they’re call stack reflection. I called them out in particular because I think it’s disappointing that they’re slowly being removed from JS.

var f = function callee(){
    if(Math.random() > 0.5 ) callee()
}

…is about as close as you can get in ES5 strict, and caller was never standardized. But I don’t like that callee is tied to what you decide to name your function.

I agree that it’s potentially troubling that any old call to foo(bar()) might not actually result in a call to bar, but I’ve settled on liking the idea. It’s sort of a power/trust tradeoff, but I like the idea as a function author that I can decide whether or not the arguments people give me are evaluated. As a function user, I might disagree with the author’s decision, or be surprised by it. At some point, though, if you can’t trust the API you’re considering, you might as well not use it all. For all you know, the implementation of foo could just be rm -rf /, which could at least trash your user directory.

In general, I tend to be in favor of “use with caution” features.

I just liked the intuitiveness of the arrow operator in that case; how it has both sending and receiving operations available depending on the spatial orientation you choose for your channel value.

I only used Go in one class, and for whatever reason it seemed like channels weren’t useful for building distributed systems.

I look forward to reading them when you do! And for those of other people who I know are reading this thread. :grimacing:

For me it comes down mostly to simplicity and comprehension. I don’t want things to be overcomplicated, especially when the compiler can do a lot of the heavy lifting. Writing your own header files? Forget about it.

The editor, too, should be a big part of the coding experience, doing heavy lifting that goes beyond just text editing. Artists don’t (usually) type in the coordinates of all the paths of an Illustrator graphic. The authoring program provides tools to make the creation of those graphics easier. Why should coders?

Now I’m not saying I want blockly, but I think code editors still have room for advancement as far as code organization goes.

First things first. Everything’s dynamic, reflective and every line or block is expression that can resolve to some value. Variables don’t have to be declared; this is done automatically when first defined. Types are inferred.

Syntax and core API. They should be easy to read, even for those with no experience with the language, but not too verbose that makes it feel too wordy or cluttered. It would be a familiar C-style syntax with some possible exceptions like not needing parens for if statements.

if x > y {
   doSomething()
}

Operators should be one character where possible (e.g. prefer dots (.) to arrows (->) for member access). Why type more than you have to?

Speaking of arrows, I’m not entirely sure what function syntax would look like, but I think having the keyword function or func is redundant. A function block would be defined by {}, but I’m not sure what separates it from any other code block. Objective C uses a caret (^) prefix, but I’m not sure I like it. Is there enough information for the compiler to know the difference based on usage?

Function blocks are closures, a la Javascript, but you should be able to specify how values are captured. Let’s say this can be done with an at (@).

x = 1
y = 2
myFunc = {
   log(@x)
   log(y)
}

myFunc() // 1, 2
x = 3
y = 4
myFunc() // 1, 4

Similarly, you should be able to specify context of the block, again we’ll say that an at (@) can be used for that. If not specified, this is assumed.

myFunc = {
   log(this.toString()) // myObj.toString()
}@myObj

Function parameters are optional, defaultable and can be named. Same with return values, with support for multiple values.

Iteration should be consolidated as much as possible. Avoid iterator APIs and run it all through a common syntax which you can code against for custom iteration styles. In other words, if you’re looping, you’re pretty much always going to see something like

for key as value in myObj {
   // do stuff
}

// or

for condition {
 // do stuff
}

Other kinds of data can be embedded in the code. Long strings, XML, JSON, whatever.

myXML = <foo>
   <bar />
</foo>

Without getting into too much of this minutia lets move on to the IDE. The IDE needs to continue to help with simplification and comprehension. A lot of this would be facilitated through code hiding and consolidation into named, expandable macros. Something like what Brackets does:

This can and should be applied to inner functions and lengthy condition statements - anything that could benefit from readability or consolidation. Consider something like the following.

if x > 0 && x < 100 && y > 0 && y < 100 {
   // ...
}

This could be consolidated into something like

if withinBounds {
 // ...
}

where withinBounds could be expanded into the condition otherwise hidden by the editor.

Function definitions could behave similarly, especially as far as private methods, or functions that abstract implementation go. This allows high level logic to be more organized and readable.


if hiddenCondition {
   hiddenMethod()
}else{
   if hiddenCondition2 {
      hiddenMethod() // same as first hidden method; hidden is also reusable
   }else{
      hiddenMethod2()
   }
}

When you boil it down, its basically a private function in the source whose definition is hidden away, but when you’re writing your code - condition or reusable block - you don’t have to leave your current context to define it. The editor handles this for you. The idea is to have code consolidation that results in highly readable code (note that “hiddenMethod” etc. aren’t good names for this, you should really have something that describes what is happen… could the editor even be smart enough to recommend naming?). And maybe as far as the source goes, this could all be handled through meta to prevent too much separation of code through abstraction.

The IDE should also be able to visually show object relationships and code flow. Something like integrated UML or flow diagrams. It should be easy to navigate between these relationships. If you’re in an if condition, you should be able to see how you got there, and where its going.

Error handling should be recognizable and missing handles identified. Organization of different error paths should be consolidated and clear, resolving any of that “callback hell” you might see around. The hiding mechanism can help with this, exposing “happy paths” above all others. Not entirely how this would look but the idea is to keep possible outcomes of an action together for better organization and understanding. You should be able to easily tell what happens when, and where it goes when it does.

So readable (and easily writable), organized, navigable code with a high visibility into behavior.

Some other things I forgot to mention.

Mapping, like looping, should be more consistent. You see mapping all over the place done in different ways, such as through hashes or collections or structures like switch-case (or if else). I’m thinking sticking to something like the hash approach should be the standard.

map = [
   1: { doA() },
   2, 3: { doB() }
]

x = 2
map[x](); // doB()

// replaces what might be

x = 2
switch x {
   case 1:
      doA()
      break
   case 2:
   case 3:
      doB()
}

There was something else too, but I already forgot…

edit:

Maybe it was about branching, and how in the editor that could be displayed in a kind of column view, side by side rather than have alternate code paths defined in various places linearly defined in the script. This especially for error handling. For example, try catch can start with the try block at the top, then all the catch components would be side by side with a finally block below all of them. Something like that.

1 Like

Agreed.

Heh, I agree, although for some reason it seems simpler to do this for artists than coders.

Haha. True. Other people in my family have used things like LabVIEW for their professional pursuits, and everybody always hears about Scratch for early programming education, but I don’t think that those systems are the way forward for programming.

In general, this is fine with me. Declarations are probably statically determinable most of the time. Type inference is nice, although it’s tricky in dynamic languages, since it takes just one little leak to break everything an inference system is trying to prove. (Even if it’s trivial to run the program once and be confident about the types of those values.)

I agree with this notion, as long as you have enough ASCII symbols and the operators you pick seem reasonably comprehensible. The C/C++ debacle over . and -> is interesting, since it ended up that they meant the exact same thing besides pointer issues, and usually those issues were non-issues due to the declared types of a function/method.

Yeah, there is a bit of silliness when it comes to that sort of declaration. OTOH, there’s another worst-case scenario when you’re using Lisp or Scheme, because everything uses the s-exp notation. I do like the distinguishment between () and {}`, although almost no matter what that’s something you need to learn on a language-by-language basis.

This is one of the more unique (IMO) concepts you’re proposing in this thread. It makes sense, in that I know what you want that syntax to do, and I can see how it would be useful. The first @x case seems easy to deal with and immensely useful. The latter case seems like a convenient way to use Function#call; it’s useful in JS sometimes, although I don’t know if it’s important to have syntax for that.

This is a pretty interesting point you’ve made. I do agree that it’s quite useful to quickly be able to see constructs like looping. Before your post, I hadn’t thought much about the importance of making it clear when you’re iterating over some collection. This sort of enforcement certainly helps things like static analysis. On the other hand, it does seem like people have started to enjoy functional approaches to iteration, like $.forEach or _.each, or whatever shorthand is around these days. Those are almost certainly trickier for CFA, but sometimes more useful for thinking about a program.

I’m a fan of this embedded data approach. Otherwise, you end up with weird file IO that is just hidden, which makes understanding programs more difficult, which is especially annoying when the tidbits of structured data are small enough that it makes total sense to include them within program code.

I like this idea, although I think it’s at least a few years off in terms of being actually able for people to write an editor that does anything remotely close to doing this well. I’m not a huge fan of code folding, myself, since it’s usually just as easy to scroll down more, and I rarely trust the ways that people describe their folded portions of code.

This sort of relationship between the code and its execution environment is probably one of the most important areas in which IDEs can be improved. Personally, my favorite effort in this space has been Code Bubbles, because the interface style of showing trace, flow, and execution-related relationships between code snippets is quite powerful.

There’s a pretty large gulf between how people write code and how people debug it, and I’m not sure anyone’s come up with an interface which melds the two modes. (In traditional HCI, modal interfaces are generally discouraged, so it’s usually preferable to come up with a single interface which can handle both situations without creating separate interfaces for each, so something like Code Bubbles seems great if people could make it more natural for day-to-day code writing.)

Your post was this first I’ve heard of this idea, but I like it. It resembles how I usually think of my code, where sure, errors can happen, but they’re usually silly rare things like discs running out of space, and it’s not productive to have that handling code in my face all the time.

This is interesting to me because there are some very traditional compiler operations built around things like the switch statement in C that make sense from a performance perspective. IIRC, if you use ints in a switch statement, the compiler is easily able to make really fast jumps in assembly which make it a great choice. AS3 didn’t even attempt those optimizations, I’m pretty sure. If you wanted to eliminate that syntax altogether, you’d probably be forced to have a really really tight type system, or discard the hope that some previously optimizable situations could be optimized.

To me, this part sounds like you want to make the control-flow graph more visible to programmers, which is probably a good thing. The tricky part is exposing this to code editors, since it’s a really really slippery slope from adding an extra dimension to resorting to a projectional (or syntax-directed) editor, like people have seen with Simonyi’s Intentional Software or JetBrains’ Meta Programming System. None of those have taken off, possibly because it’s hell for programmers using those systems.

Any time you go away from the traditional, line-by-line, ASCII-style programming language you run into the problem that you need to support and popularize whatever new editing style you’re promoting. And that’s a large hurdle.

Performance

One of the things I was trying to avoid was the affects on performance. As a pie in the sky exercise for a coding language, I felt those considerations would only be detrimental. Then again lookin back, I am still confining myself largely to syntax I’m using today. I guess if you’re really thinking out there you’re moving toward holodeck.

Functional looping

This is something I thought about but decided to ignore because it’s not something I work with much now. The language as described doesn’t necessarily prevent this either. I think rather it just encourages consistency.

Code bubbles

I remember seeing this year’s ago and thinking it was really close to something I wanted but also, in its own way, a step back. I’ll have to catch up on what’s been going on with that project.

I can definitely see my ideas for a coding environment leaning very much in that direction. I don’t even have a good picture in my head what it would look like. Im working largely off dissatisfaction of existing software and why they aren’t doing more.

My ideal programming language would be english because I already know it fairly well. I just need a compiler that’s as smart as the human brain.

So, in summary, I have nothing useful add to this discussion.

I think thats idyllic in many ways, but it could also be difficult (excessively verbose) as readability goes. Then again how necessary is it at that point? If you want something to change, just speak it. If you need to figure out why its not working, just ask the computer and it will tell you.

If you want to go that route, you might as well plug it directly into your brain and skip the language part. :money_with_wings:

[ot]Also, wow, look at that automatic YouTube embed generator!


[/ot]

Nice selection of videos to use there :wink:

Yeah, sounds about right :stuck_out_tongue_winking_eye:

…new research has discovered that only about 5% of written code captures the core functionality it actually provides.

http://www.itworld.com/article/2881655/your-code-is-far-more-chaff-than-wheat.html

Haha, yeah, Java is especially bad at that.

On that subject, I recently saw an interesting article that doesn’t take a side on the type debate, but argues against some misconceptions:

http://www.cl.cam.ac.uk/~srk31/blog/2014/10/07/

The Sniveley/Laucher talk included a comment that people like dynamic languages because their syntax is terse. This might be true. But it made me uncomfortable, because it seems to be insinuating a different statement: that people dislike type systems only because of the syntactic overhead of annotations. This is false! Type systems don’t just make you write annotations; they force you to structure your code around type-checkability. This is inevitable, since type checking is by definition a specific kind of syntactic reasoning.

(emphasis mine)

His arguments there, particularly for [the section you quoted], seem a little far reaching. What’s his point? If you use types, you must use types? Cool story, bro.

Which section, the one I quoted?

yeah

Some interesting ideas in here, but as with the first commenter, tldr (fully)

Flow! (lots of pictures)

This is an interesting thread. :smile: A couple things struck me that I want to comment on.

I’m all for defining function blocks with {}. I don’t think anything has to separate functions from any other code block because they’re semantically the same. They’re not implemented the same way for performance reasons, but a compiler can tell how you’re using the block without annotation. (If it comes after while and it’s not assigned to anything, it’s inline. If it’s assigned and referred to elsewhere, compile it as a closure.)

Autoclosures are an awesome compromise between lazy and strict evaluation, which gives you significant power to create new language features. You just have to assume that every library author is smart and sane and friendly. Which I do.

I’m also all for the consolidation features @senocular proposed. You can almost do it automatically with code that’s commented for clarity. Take kirupa’s post on merge sort in JavaScript:

function mergeSort(input) {
  if (can't divide further) {
    return input;
  }

  divide the array in half
  recursively sort and merge
}

function merge(left, right) {
  var result = [];

  order the sublist as part of merging
  add the remaining items to the result
  the sorted sublist

  return result;
}

(which isn’t perfect, but as you can see, could be super readable with a few tweaks.)

I’d like to do away with the requirement that you must name something to refer to it in more than one place. Tuples are records without keys. Why not variables without names?

The language would support Jonathan Edwards’ schematic tables (conditional logic written as truth tables) (screenshot). They make logic like in your example:

if hiddenCondition {
   hiddenMethod()
} else {
   if hiddenCondition2 {
      hiddenMethod()
   } else {
      hiddenMethod2()
   }
}

and every equivalent form, such as:

if hiddenCondition || hiddenCondition2 {
   hiddenMethod()
} else {
  hiddenMethod2()
}

all collapse to the same structure, which is easier to reason about, refactor, and maintain. (I’m having trouble convincing myself that the second version is actually equivalent to the first, even though I’m pretty sure it is. This is why notation matters…)

Also, we love to rag on Java, but I’ve been writing Java at work all day and I have to say that dependency injection is amazing. It is the programmer equivalent of reaching out from under the car, announcing, “socket wrench,” and having one materialize in your hand. As far as I’m concerned, we should be injecting everything everywhere always. Decouple all the things!

The language would also let you inject the user. If you’re not sure how to implement some behavior, inject yourself and be in charge of turning the parameters into a return value and side effects manually. You can abstract and encode that into a programming notation later…


And this isn’t an idea for a language, but it’s something I keep in mind when I’m thinking about ideas for programming languages: cognitive dimensions of notations. In particular, programming is not one activity. It’s several (writing, reading, debugging, etc). And “each activity is best served by a different trade-off in the usability on each dimension.” In fact, if I could write code in Ruby, but refactor it in Haskell, debug it in Smalltalk, and deploy it in JavaScript, that would be great.

That’s why I’m right on board when @krilnon says programming languages shouldn’t round off every corner in the name of protecting us from ourselves. Because they shouldn’t … up until the point when we move from designing and writing code to maintaining its current behavior. At that point I’m all for language features that help you not accidentally break things.

1 Like