Should a good tool/framework/language not only "make easy things easy, and hard things possible", but also (generally) make patterns effortless, and anti-patterns painful?
Yes. But there's always a good question here of how objective some doctrine separating patterns and anti-patterns is. If you're not opinionated enough then the things people can create (with the mechanisms you provide) hit complexity limits sooner. If you are too opinionated you turn people off who might work differently than you but no less effectively.
Early RoR seems like a nice case study in hitting a sweet spot of opinionated-ness.
Slightly related, but someone told me that a “good tool/framework/language” should allow you to make ugly things
I think a lot of popular environments are popular because you can do ugly things easier
It's also maybe why it's harder for environments that force you down a prescriptive path or fast path or safe path but are more rigid are maybe not as popular
Popularity comes from whether people could bear to learn them. Fewer people are motivated by perfection than are motivated by progress. Systems that only let you do it right make it harder to do anything at all, and harder to learn, which injures popularity. We learn by trial and error, not by reading docs. Feedback, not guardrails. By the time you know Python well, doing functional programming in Python is easier than learning Haskell. </opinion>
I think there's room for systems with varying degrees of opinionatedness and encouraging/discouraging certain patterns. For instance, systems deliberately aimed at newer users (consider Scratch) or narrow use cases (e.g. scripting DSL for a particular app) have a good case for being opinionated. Something to build a whole business on, less so, largely for reasons Jason laid out.
Also, even leaving aside the subjectivity, what constitutes a good or bad pattern is (at least in some cases) dependent on the system and all its interactions, particularly interactions with complicated stuff like human habits and attention. So you don't necessarily know at design time what you want to encourage. Unless, you know, it's a 1-1 analog with a pattern that produces security vulns across ecosystems or somesuch, then you probably want to avoid making that the path of least resistance...
I think you can support making ugly things and still be very opionated though. For example Scratch is beginner friendly but anyone that pushes Scratch a little bit beyond its core purpose of teaching the basics of programming is probably doing something ugly with it
And in some cases exploiting poorly defined behavior of the system to get their thing working
Pico8 is another place where people do heinous things even though it is extremely opionated
But I think you have to have some of that if you're making a programming environment where easy things are easy (you have to have an opinion on what's easy) and hard things are possible (you need to have enough flexibility and tricka to allow for ugliness)
I agree and disagree. A +tool+ should make anti-patterns painful - yes. A +tool+ should not make hard things possible. My ideal +environment+ is one which allows me to choose the most appropriate paradigm/tool for the sub-problem that I want to think about. That ideal environment should make it easy to compose solutions to uber-problems by plumbing sub-solutions together, regardless of the paradigm used. To make hard things possible, I should be able to use Assembler (line-oriented or tree-oriented (Assembler, C, etc. vs ASTs, CSTs, Lisp)) and bolt these bits into the overall solution. The last time I touched such an environment, was the UNIX command-line (#!/bin/bash, #!/usr/bin/env node, #!/usr/bin/env python, etc., etc.). The problems with the UNIX Shell are (1) it over-specifies (!) the format of IPCs by insisting that the special byte 0x0a terminate every blob of IPC bytes, and, (2) the Shell’s insistence on using 1D notation (sequential characters, leading to the unrealistic restriction that commands have exactly one input and exactly two outputs (the UNIX kernel allows more of each, but the shell does not encourage this)).
Yes and yes! 🙂
Yes, we need to have a variety of “paradigms”, which I would call architectural styles at our disposal.
blog.metaobject.com/2019/02/why-architecture-oriented-programming.html
And also yes, although Unix pipes/filters is architecturally/structurally great, and often a better fit than call/return (procedures, functions, methods), it suffers from packaging mismatch.
That’s why for Objective-S, I chose Polymorphic Write Streams, which generalise pipes/filters (and can be specialised to pipes/filters in order to interoperate).
Although I don’t mention it, that paper already mostly shows why filters are in a sense more fundamental than procedures.
One way Ruby (and Rails by both implementation and shared philosophy) manage is by realizing that if people keep trying to do things a certain way, we should probably help them — even if the edge cases are really messy.
In contrast, when functional programming in, say, Haskell, I don't feel laden with accidental complexity, but I do feel burdened with getting all my cases right.