There aren&#x27;t any, that&#x27;s the problem.  Because most language designers these days seem to think that the best way to solve problem X is to develop an entire closed-garden ecosystem that also can solve Y and Z, and ends up doing none of them well, while forcing you to use their language and only their language.<p>Everywhere I&#x27;ve worked, I have seen the same things reimplemented poorly over and over again in general-purpose languages.  Every system I&#x27;ve worked on is roughly a mash-up of database, dataflow graph, state machine, work scheduler, IPC, process coordination, programming in the large, and very low-level algorithmic stuff.  Each of these could be factored out into a common simple language, but almost invariably they&#x27;re just patterns imposed on C&#x2F;C++&#x2F;Java&#x2F;what-have-you code.  This greatly hampers understanding and analyzability.<p>There <i>are</i> rare occasions I see DSLs used to fulfill these roles, and it&#x27;s to great benefit.  Working with a well-designed PostgreSQL database rather than some half-baked home-grown object database is a dream.  You get a concurrency model that works, and you can extract graphs of your data model for free.  Same with using a state machine description language like SMC rather than hand-coding stuff as C++ boilerplate.  Same with using a process coordinator like Erlang&#x27;s OTP (which isn&#x27;t a DSL but is close enough).<p>I&#x27;m not writing these posts to try to get developers to use DSLs more often.  I&#x27;m writing them to try to get language designers to <i>design</i> DSLs more often.<p>On Coq:<p>Coq is not at all a practical <i>programming</i> language, since it has no concept of communication with external processes (i.e. I&#x2F;O).  This is <i>exactly as it should be</i>, since Coq is focused precisely on the domain of <i>functional algorithms</i> (and proofs).  And, I have found that, within that domain, the restriction that all my algorithms must provably terminate is nearly <i>invisible</i>.<p>There is a very simple general way to ensure that all programs in Coq terminate: augment each recursive function with one additional argument which is a counter which must always count down to 0 (at which point recursion must halt).  On the initial call to a recursive function, select a starting value for this counter which is greater than the number of iterations the algorithm requires.  (When proving correctness in Coq, you will be asked to prove that this starting value is in fact suitably high.)<p>I have kept this requirement in mind when designing programs in other languages, and I have found it trivial to meet in all cases.  Turing-completeness is <i>not</i> a feature I need in a programming language.