45 comments

[ 1.8 ms ] story [ 107 ms ] thread
With Java I've seen an increasing problem with programming style in large projects because of it's kitchen sink support of so many paradigms.

In addition to code readability it also effects code reviews because many cases come done to taste and it's difficult to build any type of consensus.

> With Java I've seen an increasing problem with programming style in large projects because of it's kitchen sink support of so many paradigms.

Java is pretty much the purest classic class-based OOP language of all industrially-significant languages. It has constructs inspired by those from other paradigms, especially FP, transposed into a class-based OOP context, but it's pretty much the last language I’d point it as suffering (or benefiting) from “support of so many paradigms”.

It’s pretty easy to go full procedural with static where classes function only as a name space. Also, I’ve worked in code bases that were a Frankenstein mixture of deeply nested inheritance hierarchies mixed with attempts to be fully functional mixed with C code written in Java. So I stand by my statement.
Some of those FP concepts, transposed into a class-based OOP context, like closures and algorithms over data structures, where already present in Smalltalk-80 actually.
One fantastic multi-paradigm-from-the-start language that often gets missed out is Mozart/Oz - http://mozart2.org . This language is used in the awesome book "Concepts, Techniques, and Models of Computer Programming" by Peter van Roy and Seif Haridi - https://www.info.ucl.ac.be/~pvr/book.html
This is a great and mind expanding book/System. However since Mozart/Oz does not seem to be used that much in the "real world", i think it maybe worthwhile to investigate application of the same ideas to a mix of Erlang and C/C++.
Just wanted to add my recommendation for Concepts, Techniques, and Models of Computer Programming" by Peter van Roy and Seif Haridi.

This is an amazingly thorough, comprehensive and erudite work. Highly recommended.

Mozart/Oz is so underrated. It's a shame it never took off.

AliceML was also an interesting hybrid of Oz + SML.

> Mozart/Oz is so underrated. It's a shame it never took off.

The documentation was a major barrier. More so than any other language (trying for public adoption) that I can immediately think of. A textbook-expensive dead-tree book as onboarding path. With the usual pattern of commercial thing reducing incentive to create accessible thing, without fully meeting the need for same. And online documentation that was fragmented and poorly integrated. So "looking up" a topic would involve groveling over a pile of documents, and lecture notes and such, searching out pieces of the puzzle, and pulling them together yourself. So much niftiness, so inaccessibly documented. :/ I once massaged together a distributed table of contents[1] (long since link rotted), which might give a feel for it. Part of the problem was M/Oz was at heart a teaching and academic effort - think of early Racket.

One of the hardest things about capturing history (and perhaps a fruitful way to teach it?) is the "wait, they wat?!!?". Picking up a language now, with expectations of how language and ecosystem are documented, and HN kibitzing on "the landing page should be improved by ...", reflects a big cultural learning process on how to do this well. Mozart/Oz, didn't.

[1] http://www.vendian.org/oz/wiki/index.cgi?VirtualManual

Common Lisp is the best multi-paradigm language I know.
I came here to write this, I was surprised it was not even mentioned.
Another option is DSL[0]. It takes some time to create a new language and other team members have to learn it, but long-term efficiency will be out of the roof!

Also, I think it would be nice, If there was a platform(maybe it exists?), which would connect let's say top 20 languages and it would be super easy to call function/block of code from other languages. Programmers just pick up the right language for the specific part of project.

[0] https://en.wikipedia.org/wiki/Domain-specific_language

The platform you're describing is the way I use bash (understood that you're asking for a little more integration than just exec/fork/spawn).
I think SWIG (the Simplified Wrapper and Interface Generator, www.swig.org) may be what you're looking for. It can create interface code for calling language X from language Y. I'm not sure it covers all the most popular 20 languages, but it covers a lot.
It does exist and it's called Racket [1] and language-oriented programming. You can write prolog-like languages [2], or Haskell-like [3], or even C-like languages [4].

You can even write BNF directly [5] and you can /call/ these constructs across different languages. Racket makes it /easy/ to play with semantics and syntax. You can feasibly write a language in an afternoon, to solve 1 specific problem.

[1] https://racket-lang.org/ [2] https://docs.racket-lang.org/datalog/datalog.html?q=datalog [3] https://github.com/lexi-lambda/hackett [4] https://docs.racket-lang.org/profj/ [5] https://beautifulracket.com/appendix/why-lop-why-racket.html

I used Racket at some point alongside SICP ~10y ago. I always considered it being a pure educational/experimental/research language.

While this is likely a primary focus of Racket, it seems to be there is a ton to be gained from diving deeper into it, so thank you for these resources.

In my opinion, a promising avenue for programming would be DSLs with restricted semantics + SAT/SMT solving to deliver lightweight formal methods.

So, instead of programming in Turing-complete languages, one creates many specialized DSLs with just enough expressiveness. And, in parallel, some automated formal methods. The combination of a DSL and SAT/SMT makes this feasible, which contrasts with automated theorem proving on Turing-complete languages.

Racket seems like a good platform to do this.

Yes! There is some work being done in this space already; by the incredible people at North-Eastern. One of the more interesting parts of this problem is what you do at the /boundaries/ of the different languages with differing semantics such that you don't accidentally introduce bad lemmas.

There are a few published papers which study the boundary between dynamic and static type-systems, but the hope was to extend it into gradual-typing between different type-systems .

I think I know some of these papers, but could you please point me to some of the references you have mentioned?
Of course — this paper talks about the mechanism which Racket explores to keep the boundary safe [1]. This paper [2] elaborates on some of the performance challenges. 'Shallow typing' by Ben Greenman et al. is available only by video [3] and slideshow form [4]. Finally, there's a really interesting paper which came out this year which uses a verifier to remove the some of the runtime cost [5] of sound gradual typing.

[1] https://www.cs.utah.edu/plt/publications/oopsla12-stff.pdf

[2] https://www2.ccs.neu.edu/racket/pubs/popl16-tfgnvf.pdf

[3] https://www.youtube.com/watch?v=A4AA8nweYAg

[4] https://www.ccs.neu.edu/~types/resources/talks/g-rcon-2020.p...

[5] https://arxiv.org/abs/2007.12630

I can think of two examples, only one of which is me being a smartass.

Emacs has a feature called org-mode Babel that lets you write scripts where you pipe the output from a block in one language to a block in any other language, but I don't think you could write a sizeable app this way.

The slightly more smart-alecky answer is the command line. I work in node, and it's trivial to call an external program and then operate on the output. There's nothing stopping you from writing a program that is a single function in the language of your choice and calling it wherever you want.

This is the reason why I am exited about GraalVM and its support for polyglot programming[1] (via the Truffle framework).

[1] https://www.graalvm.org/reference-manual/polyglot-programmin...

A path already crossed by both IBM i and z OS, and the CLR.

So while it is great that Java eco-system is going down the same path, we should not forget those that went there first.

Let me chime in also here. Besides racket, which is a great choice when liking lisp syntax and dynamically typed code, there is also Java which has python-like syntax and allows for amazing DSLs that are typesafe but still easy to use. Probably the best language for DSLs in production, especially if typesafety is important.
A little surprised Swift and Kotlin aren’t mentioned. Both expressly support FP and OOP approaches (although some of Swift’s FP-enabling features like protocols look more familiar in OOP contexts, similar to the common misconception that TypeScript is primarily designed for OOP).
Raku (formerly Perl6) is multi-paradigm.
This article doesn't take a fair look at where the "wars" come from at all. In my experience, the wars come from people wanting to be productive in a language but not feeling productive in an unfamiliar paradigm. It's a social problem that needs a social solution -- and the solution isn't to tell people to "reject purity." The skills and mental habits required to be productive in a given language and paradigm take time to develop, and different paradigms can have substantially different library ecosystems, even in the same language. In a language that supports multiple substantially different styles of programming, choosing a style has implications for short and long term productivity as well as programmer morale (maybe drastically different morale consequences for different programmers on your team.)

Most people won't remember, but there was a time in the 1990s when there were a lot of professional programmers who were not comfortable with OO. In a codebase, you'd get fights between people who embraced classes and utilized dynamic dispatch and people who didn't see the point of those things and were able to be extremely productive without them. Now there are a ton of professional programmers who are not comfortable with FP, coexisting in the profession with programmers who have fallen in love with type classes and use them habitually to solve every problem. It's a massively unjust cop-out to tell the former that they need to "reject purity," as if it's a moral flaw they need to fix. It's a long process of learning new skills and concepts, during which they will not be performing as accustomed at their job. People who balk at that are not just narrow-minded. People who focus on present productivity rather than future productivity are often the people who are getting the most work done, and it's important for an organization to figure out a path forward for the entire team before letting a new paradigm loose in the codebase. Moreover, it's legitimate to ask if the short-term consequences will be balanced out by long-term gains.

Also, as a response to the article trying to mix design patterns into the argument, if people want to make design patterns a thing, the idea needs a complete relaunch. Convince me that they're worth the effort. In my twenty years of experience working with other programmers, programmers who know design patterns are not better programmers than those who don't. I don't feel any less capable having 99% forgotten them. Most importantly, younger programmers who never bothered with them are just as good at structuring code as those of us who worked through the GoF book during our formative years. My opinion: just let it go.

I think you're not looking deeply enough. Sure, there are problems when people don't know a language or a paradigm well enough to be comfortable or productive in it. But once they know two paradigms well enough, the "pure vs impure" question becomes a real question in its own right, rather than a cover for "I don't know one of them well enough".
But once they know two paradigms well enough, the "pure vs impure" question becomes a real question in its own right

Yeah... "once they know," which is after their time and effort has been spent and the opportunity cost has been incurred. Investing time and effort in learning something is, indeed, an effective way of finding out if the investment is worth it, but since you find out after the fact, it can't inform the decision to make the investment or not.

Also, somebody who was hired for their knowledge of one paradigm and is currently working productively in it, as is frequently the case, does not need "cover" for not knowing a different paradigm.

Having read Christopher Alexander before encountering GoF, I am a little sympathetic to the GoF.

IMHO, the idea of patterns addresses two things: 1 - attacking the "haystack/spaghetti" that most large systems tended to result in, by providing a vocabulary or standard set of metaphors that can be used to encourage rather than discourage some good practices; and 2 - provide a common meta-vocabulary for discussing design (sometimes those "discussions" are between a long gone programmer and someone that encounters the code later).

Of course what we got instead was fetishism and towers of factory classes emitting envelopes resulting code that was diffuse, incomprehensible, and that focused on things other than the actual problem at hand. Despite my theoretical sympathies I stay far far away from the GoF.

This reminds me of the Agile movement: not that bad an idea for a technical management consulting team trying to solve a customer's tactical problem. But outside that domain only some bits and pieces make sense, and, depending on the domain, not always the same bits and pieces. Again: fetishism took over.

Yeah, I appreciate their intent, but I think they made a mistake in thinking they were near the end of programming history. They really thought they were zeroing in on a lasting, almost ideal style, but it turned out to be a style that was quickly made obsolete by changing languages, programming concepts, processes, and architectures. Also, in software, you're designing for the machine and for other programmers, and it's a lot easier to discover how well a software design performs for machines and colleagues than it is to discover how an architectural design performs for a family, a company, or a society. I do appreciate the intent, but it's hard to see why someone would still be sticking with the design patterns concept twenty years later.
I worked at a company where they had to fire some of Scala programmers. The people who were fired were functional purists, whereas a bunch of the team were coming from Java and (understandably) wrote Scala as if it were just more terse Java (very Kotlin-esque).

Long story short, the third or forth time they were caught rewriting perfectly adequate and correct code simply because it wasn't embracing all of Scala's functional constructs, they were warned. Next time, they were canned.

I've been a Scala programmer for close to ten years now, and I've been shocked at how blithely people will 1) unilaterally commit their coworkers to enormous learning curves, without regard for the consequences, and 2) use crucial projects as personal learning opportunities, again without regard for the consequences. Partly it's self-indulgence, partly it's machismo, partly it's a sincere desire to build the best thing possible, but whatever the motivation, it can cause a huge mess. People need to feel responsible for leaving a certain ratio of polished, mature code to beginner, learning code, even though that means that (gasp!) they can't live in a permanent state of learning big new things.
IMHO scala projects require company’s wide enforced coding styles. Otherwise one group will try writing it as Haskell while the other will run off with Java crap that is simply not needed in scala
That's right. And actually, once you stick to one of them, Scala is an amazing and productive language, better than Kotlin and Java are and will probably ever be. But mixing the two styles does not work out.
I agree. Vanilla OO/functional Scala, focused on immutability and functional idioms, but without effect systems and type classes, can be a really beautiful, simple, expressive, highly productive way to develop software. I'm neck deep in FP Scala now (the cats ecosystem with cats-effect) and hoping I eventually come to feel the same way about it.

I'm very sad about the Java OO extremists and the Scala functional extremists finding common cause in denying the possibility of moderate, limited use of Scala's power. Programming that way is head and shoulders above Java, and I've yet to see FP Scala match it for productivity (again, hoping and working, so fingers crossed.)

> I'm neck deep in FP Scala now (the cats ecosystem with cats-effect) and hoping I eventually come to feel the same way about it.

I think if you want to experience a similar feeling, you should look at ZIO instead.

It makes things so much easier compared to cats-effect by being less abstract and more direct, but still works with the cats type-classes. Really recommended for the functional side of Scala.

We're thinking about ZIO. It looks really nice, but we have applications built on a lot of different frameworks already, and I'm worried about how we can rein in our tendency to always add more technologies and never reduce and consolidate.
Yeah, I can see that. The good thing is that you can replace cats.effect with ZIO quite easy - not so much the other way around once you use ZIO specific features though. It's a tough decision...
I agree but it goes both ways. Bolting some over-engineered and mostly unnecessary Java pattern or framework/di on top of scala does not simplify things either. Why lifhtbend had to make guice standard in their play framework is beyond me
A superficial article, not good enough.

Better read this oldie but a goodie: Multi-Paradigm Design for C++ by James Coplien.

It is an oldie only in the sense that we have learnt new techniques on using the "template sub-language" within C++ in even more interesting ways like meta-programming etc. since then. With "Modern C++" more features have been introduced to increase the footprint of "design expression space".

This article has me torn. On the one hand, I always use multiparadigm languages at work, and have always preferred a pragmatic, "best tool for the job" approach.

On the other hand, I am starting to believe that, in truth, there are no multi-paradigm languages. Only imperative languages with bells and whistles. Because what matters isn't how you can use the language, it's how you do use the language. With the peculiar recent exception of Scala, multi-paradigm languages seem to always tend toward a mushy, homogeneous, imperative style whose most noteworthy feature is just how imperative it is. Even with all the classes, higher-order functions, and declarative DSLs sprinkled on top, the overall shape always ends up being overwhelmingly imperative. It takes more than a dollop of meringue to turn apple pie into key lime pie.

I didn't really start thinking this way until more recently, when I started looking outside of the mainstream and paying more attention to explicitly single-paradigm languages. I think maybe it's a bit like how McDonald's, A&W, Burger King, Hardee's, etc. all seem completely interchangeable if you don't spend a lot of time eating at them.

"metalanguage does not exist"