Certain properties are of course enforced so it’s impossible for a project to do certain things that make the code harder to read. On the other hand, humans are so creative that I have a hard time imagining that, given time and a wider mix of talent, you’ll still have code hygiene issues. Maybe not the exact problems that another language might have, but certainly your own flavor will be developed as developers have less contact with the core language enthusiasts that establish said best-practices..
Again, happy to be proven wrong but I’d like at least anecdotes or some kind of evidence rather than a theoretical argument from first principles that completely ignores the human element.
I can replace a sorting method having O(N log N) time with one having O(N^2) time, without changing the type. But it will certainly have an effect on my system, and if the run-time is too long, may result in an unsafe system (eg, in a real-time control system which requires a maximum response time).
While I know what you mean, "bad code" includes things like unacceptable performance.
I also find it hard to understand how to apply type systems to algorithms. What is the type of an algorithm which returns a graph diameter, and how does it differ from one returning the graph eccentricity or other graph property?
(That's where "same values for the same arguments" comes in, but that's that hard part of the problem, yes?)
Maybe they wouldn't be writing any code if they had to write it in a languages like that.
The reason most Haskell codebases are cleaner is because the culture manages to uniquely emphasize good practices such that people don't try to break abstraction barriers.
I'm sure it's possible to write Haskell code that "misbehaves" as much as python code does. Once smart people with no training are let loose on something, that'll definitely happen.
We're lucky that Haskell hasn't been attacked yet :)
No. That is only safe at compile time but not at runtime. Because the type alone often does not guarantee the meaning and semantics of the values in the specific context and program are the same.
The semantics of values are irrelevant because the function will behave identically regardless of those semantics.
This is literally what is meant by "returns the same values for the same arguments". Any function that fits this description, by definition, cannot suffer from the problem you are describing.
A language with a good type system (ex. Haskell, OCaml, Rust etc.) makes it hard to write bad code, because it eliminates a large class of potentially "bad" programs, and thus provides guarantees about the code that does type check.
If I have an arbitrary Haskell program, it might still be messy and hard to read, but I know I'll have certain guarantees about the code. For example, if a function isn't in IO, it won't have any effects, and a replacing it with a function that returns the same values for the same arguments will be safe.
Additionally, the language makes it easy for libraries to have well designed APIs, that ensure that the user doesn't forget to make the required checks. For example, removing null, and forcing Option/Maybe for functions that might fail forces the the caller to handle the failure case, instead of just forgetting to check for null, which is common.