While writing the first version of Lip, I bumped into three programming abstractions that as far as I know no other language had. This is rare in languages. Most programming abstractions were discovered about 50 years ago. Three abstractions surfacing now may be a sign of a new era.
To be clear, these aren't new programming abstractions but old ones that hadn't been fully fleshed out. I can spot an early form of them in existing work. All three can be more general.
What lead to this discovery was that I tried to write a language that keeps programs small. Keeping the program small isn't far from the best way to program.  And since the language is a program itself, I thought it has to be worth keeping the language small too. I kept reusing code and trimming code where I could until the language had me cornered. I saw no other way to do what I needed in the fewest lines of code without compromising on power unless the language had these abstractions.
1. Source code paging
The least ground-breaking abstraction is source code paging: the ability to load and unload source code on demand. 
Some languages have the ability to load code for a missing function when the function is called. When a function is missing in Perl for example, a function called AUTOLOAD runs to load it.
Languages are missing two features on autoloading. One, they trigger the autoloader for missing functions but not for missing variables. This looks like a minor detail, but it throws everything off in languages like Lisp where everything is treated the same. Missing the ability to autoload variables makes the programmer do more work. 
Two, there's no auto-unloader. If one wrote a program that continually loaded other programs, it'd eventually make the computer run out of memory or swap enough that it kills the program.  The other half of loading code automatically is removing code automatically.
These are small details though compared to the bigger problem. Before the code is loaded in memory it's loaded on disk with a package manager. Why must the programmer install manually the code that will be autoloaded later? How's that automatic if it's manual? It'd be less work if the package manager got out of the way.
The package manager is a bad autoloader in this respect. It can't add, remove, or upgrade code in memory, and it takes an extra step. An autoloader is the more general solution. Which suggests two changes to get a better language: (a) have an autoloader, and (b) drop the package manager.
The final difference is to name the autoloader a source code pager. Why source code? Why not have the autoloader work on binary?
The reason is that source code is more powerful than binary. It's less work to change source code to produce a different binary than to change the binary directly. The autoloader can still work on binaries if it needs to, but most times the programmer changes source code.
Ever got that error message
Interlisp did, with a feature called DWIM (Do-What-I-Mean).  It could notice typeos in variable names and use the variable with the closest name. If the user missed pressing the Shift key and typed a 9 instead of the left parethesis, it'd fix it for the user automatically.
The more general case of source code paging is to not only find and load on demand the source code the user asked but also to understand what the user meant when they asked. Do what I mean is valuable enough of an idea to have made it into Google search.
I added in Lip two hooks (page-fn and page-var) that get triggered when a program calls either a missing function or a missing variable. These hooks load source code while the program runs and through the network instead of a package manager, caching what they load. Paging code out is missing and there's no DWIM.
2. Code walking
A forgotten way to change source code is with a code walker: a program that changes a program. There are two ways to use it.
The first is to use the code walker as a source to source transformer. To take source code as input, change it, and produce source code as output. There's nothing running live. Let's call this a static code walker.
The second way is to use the code walker live. Right before the code executes, the code walker can change it. Imagine running a = b + 1 in a dynamic language. When the language is about to evaluate this statement, the code walker takes over and changes it to issue an update to a database.
Similar to autoloading, the general case of both static and live code walking is to operate on source code, not binary.  A code walker can change source code into binary but not the other way around.  So while for example a static code walker could take binary as input and produce binary as output, it's far from the easiest way to work. It's easier to work with source code.
Now, when would a programmer want to code walk? There's a big range of possibilities here.
Ordinarily a programmer changes source code by hand. It's less work to make a single change manually. But when the change has to happen in many places then it's less work and more reliable to make the change automatically.
What kind of change? Any kind. A code walker can change variables, array indexes, loops, conditionals — any language operator instead of just functions. It can add new abilities in existing source code, or take parts of the source code out. It's common to wrap existing primitives, like do more before and after assignment to a specific variable.  It's also common to add logging, or to change one function call for another. A code walker isn't only a wrapper like a macro but is more powerful because it can gather state from the whole program instead of only the macro call.
Can't it all be done statically? Not always, because not all of the source code is available ahead of time. The source code that ends up running might not be written by the user who runs it but by others, loaded from the network at runtime, and from locations that are unknown when the program starts. The web works this way. So it seems powerful to have a live code walker. 
I can't help but wonder how well it'd work to run the web on code walkers. Rather than just run a web app, tweak it before running it. Change not just what is sent to the browser, but also what runs on the server. It's kind of paralyzing to think to what extent source code could be changed if one could access all of it. Bugs could get fixed automatically. Algorithms optimized. Monoliths parallelized. This must be worth a try.
Most code walkers so far worked statically and were written as third party libraries.  But because the general solution is to also have a live code walker, I added this ability in the language core.
To get a live code walker, I added in eval the ability to look at a code-walkers hash table that has code walker functions for the language operators. Rather than evaluate the code, if this variable exists Lip calls the code walker with the source code as a parameter and evaluates the code walker's result.
To get a static code walker, I added in Lip a new operator called walk. Internally walk calls eval with a flag that means don't evaluate the code when eval recurses in it but instead return source code.  The static code walker also uses the code-walkers variable.
3. Safe mode
Another abstraction I haven't seen in other programming languages is safe mode: the ability to run untrusted code safely.
I'd like to hand off some code to the language and say, there, run it, but make sure the code doesn't harm anything. I don't want the code to accidentally wipe out my hard drive. I want code written by others and sent over the network to run safely on my local machine.
This feature seemed to be the safest if hardcoded in the language core. If it were implemented outside as a library, a bug in the library or how the library is loaded can break safety. Browsers got hacked this way. I don't want to take such chances. I want the guarantee of safety.
I added this feature in Lip as extra parameters to eval. In Lisp eval takes as parameters the expression it will evaluate and in some dialects an environment. In Lip it takes more parameters, one of which is an access control list that tells eval which language operators and functions it's allowed to use.
For example, to lock Lip down to run only as a calculator: 
I realize there's not much new here. All these abstractions already exist in one form or another. But once I made all three more general and put them in the same language I started to think about the options they open up.
One option is to remove abstractions. I don't see why the abstraction of a client and a server must exist. It's easier to think of an application as if it runs on a single cpu. Some types of private code will still need to run only on some machines, but other than that, a client and a server that can't harm each other are both one and the same. With safe mode they can be. To fix problems with latency, add caching at the right places. To make the program run on other machines, rewrite it with a code walker. I haven't tried this.
Same goes with the abstraction of a network protocol. Why have an application-level network protocol? Given a basic protocol like TCP/IP, an advanced protocol is a closure as source code sent from the client that runs on the server under safe mode. 
I tried removing the abstraction of an application-level network protocol in LipOS, a middleware I wrote in Lip that pages source code over the network. I had to add one more feature to safe mode: the ability to drop its privileges.  With this change closures written on the client ran safely on the server, with the server eval controlling which operators can run.
If all of this turns out to work, that'd be five abstractions that work differently than what we're used to. I can't possibly know if this is how people will want to program in the future but I don't want to go back to programming without these options.
Thanks to the reviewer who pointed me to a couple of past references to look at.