Of course it’s the polar opposite of self-documenting, legible, understandable, easy-to-maintain code.
I'm glad we never needed to learn these forms in school:
horizontalDisplacement = -(linearCoefficient +- sqrt(linearCoefficient^2 - 4 x quadraticCoefficient x constantCoefficient)) / (2 x quadraticCoefficient)
triangle_hypotenuse_length^2 = triangle_vertical_length^2 + triangle_horizontal_length^2
I'm not. You vastly underestimate how much easier that is to read, especially for students in school.
My first thought upon reading those was... my god, if that’s how I’d first learned it in 7th grade, it would have made so much more immediate, intuitive sense to me and the rest of the students!
We’re taught so many formulas where we don’t have the slightest intuitive idea of what the variables mean or the effect they have (unless we’re naturally really intuitively good at math, which only a very small proportion of students are).
But if they said it right in their name... that seriously would have been a game-changer for a lot of students, I think. A gigantic help for understanding how to connect formulas to practical word problems.
Because the syntax favours terseness for readabilities sake (to see structure and patterns rather than reading a description of the variable).
Because Python specifically favours verboseness and has many principles around using long names, underscores instead of camelcase and the like.
primes = 2 : filter isPrime [3..]
isPrime n =
let divisors = takeWhile (\d -> d * d <= n) primes
in not $ any (\d -> n `mod` d == 0) divisors
strongPrimes =
let primeTriples = zip3 primes (drop 1 primes) (drop 2 primes)
in [b | (a, b, c) <- primeTriples, b*2 > a + c](In practice, most—although not all, admittedly—of the Haskell codebases I've worked with tended to be a lot less dense than the occasional combinator-heavy sample code you see in blog posts and comments.)
scanr is a reduce function that produces a list of the reduction steps rather than just the final output. So I'm processing the list of primes and punting out (Maybe i, i i) at each stage (I could have included the type signature for "g" but I was deliberately going for a perl-like style.)
fst3 just takes the first element of a triple i.e. Maybe i. mapMaybe maps and throws away the "Nothing"s. Think of those as nulls.
So, returning to g, it returns a (Maybe i, i, i). The Maybe i is the prime if it's strong, Nothing if it isn't. The other two are the previous two primes. So what g does is a) compute the first element and b) copy the new prime (n) into the first location and the previous prime (p) into the second location.
"<$ guard" is a bit of a Stupid Dwarf Trick in all honesty, but the effect is: the expression on the right is a boolean expression. If it returns true, the expression on the left is returned. If it returns false, "Nothing" is returned.
If you are interested, I highly recommend working through this: https://www.cis.upenn.edu/~cis194/spring13/lectures.html (and that means doing the exercises). Haskell's a funny language in that there's a fairly easy way of doing things that would read how you would expect, and a whole bunch of "oh, but this is more elegant" things you can add on top. Thankfully, some of the most cutting edge features like ApplyingVia are actually producing _more_ readable code, not less.
strong :: (Integral i) => [i] -> [i]
strong λ@(_:b:c) = view _2 <$> filter p (zip3 λ (b:c) c)
where p (x, y, z) = 2 * y > x + z
Also, it hangs with scanr (scanl works) and includes 2 which isn't strong.