Most languages out there including dynamic ones have built-in IDE support that tracks refactoring down to comments.
Don’t know what the state of Haskell refactoring is now but it looks like until very recently “great refactoring” was “change one thing and then manually fix compiler errors”.
Change one thing and manually fix compiler errors it's what I'm talking about.
Point me to something like that, for, lets say, Python. For example to error when I misuse return value of a function. Or when I decide that this string is no longer a string but a customer name. Or when my function will call IO operation, but it's not allowed to do so. Or when I add new field to a record and its not fully constructed somewhere.
All of the above are a combination of of trivial automated code refactorings and some manual work because there's no silver bullet:
* writing terrible code to get something working, then breaking it down and putting it back up as something production ready
Automated: Extract method, move code, pull members up/down, rename etc.
* changing core data structure and updating all the code that uses it
Depending on what exactly you're changing. From as simple as automated "update type signatures of all affected functions and methods" to automated "rename" to yes, hunting down compiler errors
* solving some problems in separate projects and combining them together and
* copy-pasting code from another project and adjusting it to a current project
Once again, depending on what and how you combine, using the things from above.
There are more powerful tools than "oh, let's run the compiler for each change and manually fix all the errors". The flow should be "let's run all the automated refactorings and then run the compiler to see if those missed something". And these automated refactorings are often reversible with a simple Cmd/Ctrl-Z.
Ah, yes. And many of these are available even for dynamically typed languages (but not as powerful and sometimes with caveats, for obvious reasons).
Yes, Haskell is worse with those "trivial automated" (due to worse tooling, nothing about the language prevents that), but imo better with those "hunting down compiler errors". And I take easier manual work on hard problems over trivial automation any time.
Java and Python are simply not languages that encourage (or even make possible) writing code that is easy under those changes. In Haskell you have immutability (if something is declared, that's its final value, so I can simply move it around), purity (code that is basically context independent), dumb data structures (no "enterprise boolean" with nontrivial internal state and ceremony to initialize), functional (no code attached to mutable data that may implicitly require data to be in special state), no nulls in flow control (that are invisible to a compiler, so yea good luck moving function around that expects no nulls), ergonomic creation of data/types (so you don't have strings/integers everywhere that are opaque to a compiler).
There is no contest, really. Maybe with some modern languages like Rust or TypeScript, but I haven't tried those.
> And I take easier manual work on hard problems over trivial automation any time.
However, most changes/refactorings I usually encounter are due to trivial problems that can be automated. And I'd love to have those tools for FP where a lot of changes are still just that: rename a function or a field in a data structure, extract a function, rename/move a module, update type signatures etc.
However, it looks like the stronger the static typing, the worse are the tools :) Not enough manpower to take care of them, perhaps?
Yea... that's a trade off, unfortunately, and I won't pick sides here. Stronger static typing has so much potential for tooling, but none of that is reality, sadly.
As for Haskell, I think, it's a combination of manpower (so popularity, really) and the fact that the compiler wasn't developed with tooling in mind.
Thanks for conversation, stranger, and have a nice day!
> most changes/refactorings I usually encounter are due to trivial problems that can be automated
This is interesting because another popular argument against Haskell is "Haskell's type checker can only check trivial properties that can be automated".
Either automating trivial things is valuable or it's not, and the anti-Haskell camp seems not to agree on which!
I don't disagree with the "no silver bullet" quote, but you can't answer every question with that phrase. I also agree re TDD, etc.
But your comment is still odd. Refactoring seems to be a core tenet of modern programming (regardless of TDD). In TDD in particular it's a central part of how you work, and so your remark sounds odd unless you also claim you don't believe in TDD :)
Do you understand the difference between extensibility and refactoring? For the purposes of flexibility extensibility is more desirable than refactorability (to me and my problem space anyway).
>because we are talking about your comment on Haskell.
I am not in the habit of commenting about things in a vacuum.
I am comparing Haskell to other tools in my toolbox.
I am sure that in the Haskell universe (isolated from the rest of reality) Haskell is the greatest thing ever.
I mean adding setters/getters to existing data types.
It's solved in any language in which mutable data structures don't require a Rube Goldberg machine like lenses.
e.g languages which optimise for expressiveness over purism.
Languages which give me the expressive power to disregard the 'law' of non-contradiction if I happen to think it's a good idea to do so for whatever reason
https://repl.it/repls/RoundRelevantExternalcommand
In any case, adding getters/setters doesn't imply extensibility and doesn't apply to FP languages (or rather: in FP languages you can add more functions). Getters/setters are often an anti-pattern anyway. Maybe you have a better example? In any case it's very easy to "extend" types in Haskell, it just looks different to what you'd do in Ruby.
As for expressive power, are you familiar with the "expression problem"? [0] There's almost always a trade-off your language is making.
I don't understand what you mean by law of non-contradiction in this context.
I meant getters/setters are an antipattern in OOP languages. In FP there are no getters or setters, so the question is meaningless.
Ruby is not more expressive than Haskell. In fact, I'm pretty sure it's less expressive, because it cannot say everything Haskell can, but Haskell can say everything Ruby can.
You say you understand everything is a trade-off but then you make claims about lack of expressivity ;) Do you know the expression problem?
Still don't understand your non-contradiction example.
Neat. How did you do that? (Seems like something a language ought to forbid, anyway).
edit: ok, from what I've deciphered from your Ruby code, it's nothing special. You've declared a global boolean and every invocation of function "p" flips the value, so it's only a parlor trick and nothing to do with the expressivity of the language. Any language with side-effects and mutable globals will achieve this trick in the same manner. It's a trick because your first "p" and your second "p" aren't the same "p", so there's no real violation of the "law of non-contradiction".
What about the rest of my comment? The expression problem? That getters/setters are an anti-pattern in OOP languages (just ask Alan Kay...)?
> Seems like something a language ought to forbid, anyway
> What about the rest of my comment? The expression problem?
Navigating around the expression problem is precisely what I am demonstrating. A language ought not [1] decide for me what I am allowed to express or not. I have a healthy disregard for linguistic purism [2].
Expressive power is the ability to say whatever I want to, whenever I want to. That is precisely what a para-consistent logic buys me - expressivity [3].
This is counter-intuitive to most people who have been taught to value consistency above all else (mathematicians, logicians). Non-contradiction is an axiom, not a law - it's a false authority. A man-made deity.
> it's nothing special
I didn't claim it's anything special - I merely claimed that I can say it. I did say it.
If "Haskell can say everything Ruby can" then go ahead and say it in Haskell. I am not saying it's impossible - but I a curious how you might navigate the Turing tarpit [4].
>It's a trick because your first "p" and your second "p" aren't the same "p"
First p and second p? You mean p@time(1) and p@time(2)?
Are 'you' not the same 'you' as the 'you' from 1 second ago, despite the ongoing changes in your body?
Ironically, that is the disconnect between pure functions and reality. Side-effects are the norm, not the exception.
>That getters/setters are an anti-pattern in OOP languages (just ask Alan Kay...)
That's an appeal to authority. Alan Kay doesn't get to make decisions for me. He gets to voice his opinion - I get to evaluate the pros and cons. The choice is always mine.
Like I said - In my problem-space lack of choice/control is an anti-pattern. Humans get the final say - not algorithms.
You're being disingenous. You are not really breaking the law of non-contradiction. At first I thought you at least had redefined the "not" or "and" operators, but you merely wrote a function which alternates between True and False with each invocation, using global state. You've written "true ^ !false == true", which of course violates no law. Hence: trickery. You're not expressing anything novel.
This can be done trivially in Haskell making the global param explicit, and I'm sure with some trickery like the State monad you can even hide this. Maybe the syntax will be less neat, but this is a good thing: "hiding" things like side effects is bad.
The expression problem relates to your assertion about adding getters/setters, and judging by your reply it seems you didn't understand this...
So you're dismissing Alan Kay with no good reason. Furthermore, getters/setters have nothing to do with either extensibility or expressiveness. Why should I pay attention to your opinion?
Then implement your operators. Then everything is what it is defined to be. How can this be worse than sneakingly changing semantics of already defined things?
In a free world it should be legal to sell copper as gold, call the lies truth, and mix newborns in hospital - and people are just stupid if they want laws against these... ;)
Strawman. You are conflating denotational semantics [1] with operational semantics [2].
In the free world, a pilot is free to recognize that the 'already-defined autopilot' is doing something stupid/dangerous (despite all the green lights) and is able to take control of the system at run-time - he doesn't have the luxury of fixing this bug at compile-time.
In a free world people are allowed to "sneakily change their minds" when they see an obvious denotational error.
Beware of bugs in the above code; I have only proved it correct, not tried it. --Donald Knuth
Type safety is not the same thing as system safety. The latter cannot be formalized - that is why humans are in charge. Not algorithms.
Making laws against airplanes crashing is not the same thing as stopping airplanes from crashing.
Here is a relevant extract from this paper[3]:
"All ambiguity is resolved by actions of practitioners at the sharp end of the system. After an accident, practitioner actions may be regarded as ‘errors’ or ‘violations’ but these evaluations are heavily biased by hindsight and ignore the other driving forces, especially production pressure. "
> Strawman. You are conflating denotational semantics with operational semantics
But your trick is neither. The law of non-contradiction refers to the same proposition P in both positions, simultaneously:
> "To express the fact that the law is tenseless and to avoid equivocation, sometimes the law is amended to say "contradictory propositions cannot both be true 'at the same time and in the same sense'" [0]
Yours isn't "at the same time" because each evaluation of P (which in your case is a programming function with side effects, neither a proposition nor a mathematical function) depends on the other evaluation to cause a side effect.
So you're cheating, you haven't expressed anything innovative or outside the realm of other languages (including Haskell), and you haven't broken the law of non-contradiction.
>The law of non-contradiction refers to the same proposition P in both positions, simultaneously
You are describing quantum superposition, but then you are straddling the classical and quantum paradigms when interpreting the LNC.
How many instructions/CPU cycles does it take to evaluate p ∧ ¬p? More than 1? Then what do you even mean by "simultaneously"?
>which in your case is a programming function with side effects, neither a proposition nor a mathematical function
You are making my argument for me. The LNC is an axiom of Mathematics/Logic. It's not a law as in a law of physics.
And so if a physical system happens to violate it - it's hardly a big deal.
The real world actually contains things which are in two (or more) states simultaneously. We call them cubits and we use them to represent uncertainty.
What I have shown is a mutating getter. It's not supposed to be novel or original (I don't know why you are measuring me up to such ludicrous ideals). I am simply demonstrating to you a real-world scenario in which interacting with a system alters its state (deterministically or otherwise) which allows me to do the "impossible": evaluate p ∧ ¬p as True. It's just a race condition.
Side-effects are the norm, not the exception throughout the universe in general. Your classical computer would have no CPU registers, no cache, no memory, no persistence without side effects.
If the Mathematical religion frowns upon side-effects, why should I care about it when it clearly doesn't correspond to the universe I live in?
And if you are upset about my "trickery" (oh no! Physics is cheating!) here is an implementation that doesn't mutate global state.
> What I have shown is a mutating getter. It's not supposed to be novel or original
A mutating getter doesn't violate the laws of non-contradiction, because a getter is not a proposition. In fact, if you use a getter at all, you're outside non-contradiction. The rest of your post is ridiculous, but feel free to keep equivocating this with quantum physics or cubits or whatever, instead of acknowledging that rather than breaking the law of non-contradiction, your real assertion is "my language can mutate global variables behind the scenes". Much less impressive, right?
Also, you keep avoiding to mention what your "problem space" is. I suspect this is because your "problem space" is rather pedestrian and doesn't require anything you claim it does.
Not upset, by the way. That's chicanery on your part. Usually done by people who know they are losing the argument ;)
> A mutating getter doesn't violate the laws of non-contradiction, because a getter is not a proposition. In fact, if you use a getter at all, you're outside non-contradiction.
Q.E.D you are admitting that non-contradiction it's an axiom of language and not an actual law. As in law of physics.
I don't expect you to answer my question re: the number of CPU cycles you consider as being a "simultaneous" evaluation of p ∧ ¬p least you had to admit the LNC is incoherent ;)
> That's chicanery on your part. Usually done by people who know they are losing the argument ;)
Isn't this what somebody who knows they are losing the argument would say ? ;) But if you are in it to win arguments - have a noddie badge. You win The Internet.
My problem-space is the CAP theorem. Global-scale distributed systems. Where most of the complexity is 'side effects'. But we call it network IO/communication/persistence/API calls etc.
Haskell would add zero value, because the bugs are rarely about type errors, and predominantly about Byzantine failures.
> Q.E.D you are admitting that non-contradiction it's an axiom of language and not an actual law. As in law of physics.
But it was you who introduced the law of non-contradiction in this conversation, then wrote a program that had nothing to do with it, and when called out you started with this nonsense about physics, cubits and what it means for "you" to be "you" and other unrelated topics. Either your program is about the LNC or it isn't, and if it isn't (as it turned out to be the case) why bring it up in the first place.
The CPU thing is nonsense and I don't see what I have to answer about it. In contrast, the Expression Problem is directly related to your initial assertion about extensibility, but instead you talked about something else. Getters/setters aren't about extensibility, by the way.
Finally, Haskell excels at IO, network, API calls, etc. People write distributed sysyems in Haskell -- why on earth would you think otherwise?
> It's so pedestrian, it's probably your home page.
I don't understand this. It reads like an attempt at trolling but I don't understand what it means. In which sense should I be offended? Google is my home page, does that help you somewhat?
I did do that. With the intent to determine whether the discussion is about denotational semantics (theory) or operational semantics (practice).
In theory there is no difference between theory and practice, but in practice there is...
>Either your program is about the LNC or it isn't
>The CPU thing is nonsense and I don't see what I have to answer about it
Until you decide on the 'CPU thing', I don't see how you can possibly decide whether ANY program is about the LNC or not.
Denotationally - it clearly is about the LNC as defined. It expresses precisely "P ∧ ¬P".
Operationally - it is about the LNC IF and only IF "P ∧ ¬P" can be evaluated simultaneously.
If your notion of "simultaneously" doesn't mean "evaluated in a single CPU cycle/instruction" then explain what you mean?
The leaky abstraction of CPU time is somewhat inconvenient to functional purists. As is the fact that strongly-typed Haskell reduces to untyped machine code.
Somewhere between typed and untyped Lambda calculus lies the choice of "right tool for the job".
>Getters/setters aren't about extensibility, by the way.
Would this be a good time to call out the fact that all you have been doing is Socratizing? Saying that X is not Y, says nothing about Y.
You asked me to give you an example, and trivially extending an object-model to support new fields in unstructured (read: untyped) data is not good enough for you.
If you think 'extensibility' has some universal/objective /theoretical meaning devoid of practical context, then go ahead and tell us what that meaning is.
> Finally, Haskell excels at....
How are you measuring that and against what objective standard for 'excellence' (or lack thereof)?
>People write distributed sysyems in Haskell.
People write (and have written) distributed systems in all sorts of languages.
In what measurable way is Haskell better?
>Google is my home page, does that help you somewhat?
Only in so far as Google doesn't use Haskell.
If Haskell offers value at that scale/complexity, then I imagine "building a better Google with Haskell" is a great idea for a startup?
Yeah right! I don't have to refactor extensible code.