Anyway, the majority of the programming community tends to dislike that sort of freedom, preferring maintainability and the ability to work as a team where every team member isn't off creating their own dialect.
Anyway, the majority of the programming community tends to dislike that sort of freedom, preferring maintainability and the ability to work as a team where every team member isn't off creating their own dialect.
The majority of the programming community doesn't use Lisp.
Lisp metaprogramming is very different to metaprogramming in other languages (extreme opposite example: C preprocessor macros). Lisp homoiconicity makes macros easy to write, and easy to maintain.
The majority of the programming community dislikes that sort of freedom, but the overwhelming majority of Lisp programmers considers it an essential freedom.
After all, we're all doing high-level programming. We need tools to abstract stuff, and the more complex the problem is, the more powerful the abstraction features should be.
The state of the art in abstracting stuff in 2018 is still either hindley-milner languages (ML family), Prolog and friends (Erlang), or Lisp (Common Lisp, Scheme).
Oh, and while very unconstrained in some sense, the syntax also strongly "suggests" some sort of applicative model, so call/return again.
So maybe too constrained where it shouldn't be, and too unconstrained where it would need to be more constrained.
1. Dataflow
With the popular variant pipes + filters. There is also synchronous dataflow, which can be compiled to state machines. This has recently been bastardized as "FRP" (distinct from Conal Eliot's original FRP).
What's kind of amusing is that traditional programs typically get translated to dataflow variants twice: first, the compiler turns procedures into a dataflow graph and generates code from that, later most OOO speculative processors turn the computation into more or less a dataflow graph again.
2. Asynchronous messaging
See Erlang/Actor model. You send messages (calls), but they don't return.
3. Implicit invocation/broadcast
You send out messages/broadcasts. Clients can register. There is no 1:1 correspondence between call-sites and receivers. May also be asynchronous (but doesn't have to be).
4. Constraints
Both dataflow constraints (see spreadsheets) and "solver" constraints. You just specify the relations you want to hold.
5. Coroutines
Normal call/return runs the callee to completion. Coroutines interleave. The Floyd paper mentions nested coroutines, which I have to look at.
etc.
A good reference is Software Architecture: Pespectives on an Emerging Discipline by Shaw and Garland
Seriously, I replied to the second part of your comment. Lisp may be the pinnacle of DSL-friendliness, but, as you say, plenty of languages have various measures of metaprogramming support, and yet, as you note, those facilities tend to be universally disliked by the mainstream programming community :).
(I believe that another strong reason for this is that the mainstream programming community is, for better or worse, eternally stuck in the "cater to the newcomers" mode. Effective use of metaprogramming requires a deep understanding of more abstract principles behind programming and complexity management, and are indeed dangerous in hands of beginners.)
The idea, as I see it, is that they think that at a later date they can go "I'm not an intermediate programmer, I'm an expert because I have used advanced language features like metaprogramming". At the time they will probably not admit that this is a surreptitious attempt at career building.
Metaprogramming, by its nature, is sort of like amplified programming - it can be very powerful and save on tons of boilerplate if you use it at the right time in the right place. However, with that power comes a greater potential to create an almighty steaming pile of code shit that is way more horrible to reason about than even the worst code written by the most junior programmers. The intermediate programmer will likely not realize this.
Moreover, the intermediate doesn't actually have the kind of problems it solves. Metaprogramming is useful for building frameworks and I've seen it put to good use there on heavily tested open source which has hundreds of eyes on it to spot every issue and keep its natural tendency to get out of hand in check. But, however much many developers really want to believe otherwise, building frameworks is a very niche thing. It needs to be done well, but it does not need to be done often.
So the intermediate programmers then end up creating a glorious code turd of magnificent proportions in an attempt to prove that they are really experts. This creates code that gives juniors and experts alike nightmares. The juniors get conniptions because they think that they're just too dumb to know what is going on. The expert will (hopefully) be furious and demand that the shit is cleaned up. Hopefully at some point the intermediate will achieve the zen moment of "knowing when, not how to use metaprogramming is the truly important skill".
I'm a bit embarrassed about it now, and it's exactly as you said: The older devs were quite visibly annoyed with me, the less experienced ones deferential :) I realized my mistake after I had given my notice at that job and had to walk another dev through a module I had written :O
I'm much better now. I've been programming in Clojure for almost 2 years, and I don't think I've ever once used a defmacro in my own code :)
I don't think it's such an easy problem to solve because, whatever X is, developers will probably have to be a fuck up at using it first. Then they get good at it. The higher pay is going to keep them motivated to use it and it's not usually practical to tell programmers that they should put their career development on hold because it's torturing your architecture.
Furthermore, sometimes X will more or less do the job ok even if it wasn't strictly necessary. In that case, was it really such a bad thing?
Anyway, I don't see it as something to be embarrassed about. Everybody screws up and learns from it.
Sure, you can have conditionals but you can also replace them with no-ops and sure you can have no-ops but you can also move the lower part of the code up.... or should that be the upper part down? Which one is the bigger? How to keep track of this modification? surely moar code morphing is the answer?
http://journal.stuffwithstuff.com/2013/07/18/javascript-isnt...
"Modeled on" <> "is"
Brendan Eich admired Scheme and was still very familiar with SICP at the time. Perhaps "inspired by Scheme" would clear up the misunderstanding?
My question was about creating DSL's in JS. Is it simple enough that people actually do it in professional software development?
If it is, then I call into question the assertion that the majority of the programming community doesn't want to use languages giving them that much freedom.
There are some interesting things people have done with fake operator overloading: http://2ality.com/2011/12/fake-operator-overloading.html
I've not seen a JS DSL attempt that wasn't merely a "toy", but there's probably more room to explore ideas than people give JS credit for. You could also argue that there are plenty of DSLs out there as things like Babel plugins.