We abstract so we can attain functionality, rather than adding patches to attain it.
Also the fruit of abstraction -
Ability to reason about by humans (debugability).
Ability to reason about by machines (new functionality can be attained by machine calculation rather than manual coding).
Are you sure you don't have cause and effect mixed up? Good abstractions tend to fall out of OnceAndOnlyOnce and RefactorMercilessly.
Could you explain further? I don't see a connection between your assertion and my own.
We abstract, not to attain functionality, but because we already have functionality and we want to describe it OnceAndOnlyOnce. Historically, nearly all useful abstractions are developed in this manner - i.e. by recognizing some common pattern in what works, and giving it a name. Abstraction, in this sense, is the fruit of functionality. You have confused cause and effect, correlation and causation.
I believe you have it backwards. DSLs and logic programming, for example, are a means of abstracting to attain new functionality.
DSLs are, in the common case, constructed based on some prototype API or framework that has already been developed. In that sense, they do not attain new functionality, even if they receive a new implementation. Frameworks, in turn, are in most cases abstractions constructed and refined from useful patterns recognized as common across several projects.
Refactoring supports my assertion. In refactoring, the functionality precedes the abstraction, and no new functionality comes from the abstraction. Refactoring is clearly a case for which the biological metaphor should be: FunctionalityIsTheRootOfAbstraction - root, not fruit. This is why I mentioned RefactorMercilessly earlier.
- I develop most of my DSLs on paper. I do so because I anticipate that a problem is best solved with a DSL. Refactoring to DSLs is certainly possible, but not the only way to do it. If you can design it right the first time, that is the best case scenario. Refactoring is great, but I shudder if that is your only design tool.
- Refactoring isn't the only way to grow abstractions out of functionality. If you use previous experience or implementation efforts (even from other projects) to guide your DSL, you are still, fundamentally, discovering abstractions in prior functionality.
Also, you seem to assume that there is only such a thing as refactoring but no such thing as factoring. You can implement OnceAndOnlyOnce
on the first try if you get your abstractions right (which is hard, but not always improbable). In that case, functionality literally follows abstraction. --BryanEdds
True OnceAndOnlyOnce is unlikely even if you develop a relatively effective ('right') set of abstractions: there would never be a need for SoftwareDesignPatterns or BoilerPlateCode if our languages easily accommodated every abstraction and extension. And I would say that for any non-trivial project it is improbable that you will choose effective abstractions unless you have learned from similar, prior efforts (potentially from different projects). After all, you rarely know all your requirements and desiderata up front, which inevitably forces you towards a more incremental or cyclic development model, and it is difficult to develop a set that will effectively accommodate this future growth (cf. PrematureAbstraction, CodeChangeImpactAnalysis).
If you're having trouble getting a design right on the first try, you may need more experience in the domain or to consider using a better programming language. C-family languages are notoriously bad at accomodating development of abstractions. Lisp, for example, is at the opposite spectrum.
If you think you're easily getting a design right the first time, you might need higher standards for 'right', or a tougher problem. I happen to favor Haskell, which is among the superior languages for modeling, abstraction, and DSL development.
What makes a language difficult to get abstractions initially correct is the power of its static typing system (BondageAndDiscipline
). As you know, Haskell one of the more powerful static typing systems available. Lisp, Python, and Ruby, OTOH, make it a lot easier. So I think most of your argument is true but irrelevant and the relevant part (which I will address) is nonsense. We do not grow food so that we can plant it, and we do not implement functionality so that we can attain software abstractions. Thus, functionality is the fruit of abstraction, not the other way around. And again, DSLs and logic programming are fundamental forms of functionality that cannot be acheived without abstraction. The only reason the machine can reason about higher level expressions like in Prolog is because the abstract language was built in the first place. If a DSL is arrived via refactoring that is fine, but it's entirely irrelevant. Either way, the motivation for greater abstraction is the new functionality we're after.
It is true that we gain many benefits from abstraction, but you jump illogically to the conclusion that what we gain must therefore be 'functionality'. I would suggest that the primary 'fruits' of abstraction are maintainability, flexibility, and reusability - all of which contribute to productivity.
Perhaps my argument is just unclear. I am not trying to say that only functionality may come from abstraction. I am saying only that the only way we should attain functionality is through abstraction, rather than hacks and patches. I am trying to say that introducing abstraction to implement functionality should never be considered 'a waste of time' (as would be argued by many clients and undisciplined engineers). I'm not trying to say that abstraction cannot legitimately done for other reasons.
Perhaps I should rename this - FunctionalityIsAFruitOfAbstraction.
I'm saying that functionality almost never comes from abstraction. I.e. that you're wrong, plain and simple. Even with top-down BigDesignUpFront, you'll envision the functionality before choosing the abstractions to organize it. FunctionalityIsTheRootOfAbstraction. Not the fruit. Not even 'a' fruit. Abstraction is done for many legitimate reasons - to organize, to simplify expression, et cetera. But if all we had were abstractions, we'd have no implementation, no functionality.
I think you have 'functionality' confused with 'user requirements'. The functionality I'm talking about is the implemented kind. Otherwise, all functionality in all your programs must start out inlined an existing function or class, then refactored out to a new abstraction later. I have no idea why anyone would write all of their code like that - it's extremely inefficient when the abstractions are obvious (which they often are). Even when designs often turn out wrong or incomplete, it's still best to design a little before jumping into implementation.
Re: 'functionality' confused with 'user requirements' - I understand functionality to be an external property - something useful and useable, closely related to user requirements. If you're talking about the hidden behaviors that exist below the level of Alice's interface and contracts, then (as far as Alice is concerned) you aren't talking about 'functionality'. To discuss functionality and abstractions together, we must be discussing abstractions and protocols that we use in some API layer (granting that applications may layer many APIs for different classes of developer or user).
Re: all functionality in all your programs [...] inlined then refactored - This is untrue. Neither programs nor abstractions are developed in a vacuum. Libraries, frameworks, pluggable architectures, programming models, protocols, even DSLs and new programming languages - such abstractions tend to be based on functionality of earlier projects and designs. I gain abstractions from my programs, your programs, influential historical programs, et cetera. It is unreasonable to understand abstraction or functionality through the soda straw of a single program. As an independent point, your proposed dichotomy between 'inlining' functionality and introducing abstractions is false; we can also increase functionality by introducing objects or data - which is the whole motive of PluggableArchitecture and AlternateHardAndSoftLayers.
- What is introducing an object other than abstracting?
- Introducing an object doesn't imply introducing any new abstraction. E.g. we can get new functionality by adding an agent to a MultiAgentSystem. The agent is an instance, not an abstraction, and its introduction does not need to introduce any abstractions to any users or developers in the system.
- I think we come to the problem - we don't agree on what abstraction means. Abstraction means that you've introduced a self-contained bit of functionality and have given it a name. Any time you create an object or method, you've abstracted.
- I did not suggest that said agent has a name. And creating a new object or agent is NOT creating a new abstraction. The 'interface' for an object could be said to be an abstraction (e.g. AbstractBaseClass) but you don't need new interfaces to introduce new objects. I posit you confuse abstraction of functionality with encapsulation of it.
Re: inefficient when the abstractions are obvious - I assume that most of our 'obvious' abstractions will have been refactored and refined from the functionality of earlier projects or designs, and thus proven useful. In the very few remaining cases, a rationalist should certainly know better than to confuse 'obvious' with 'right': a lot of obvious things of yesterday are ConsideredHarmful today. And things are often only 'obvious' with benefit of hindsight.
My conclusion is that there was no consensus reached since the person arguing with the topic starter failed to have a good definition of abstraction. An abstraction in software is self-contained mechanism that is given a name. If it's not a variable and you've given it a [name?], then it's an abstraction.
An abstraction is a concept or idea not associated with a specific instance. There are two common forms of 'abstraction' generally recognized in computer science: parametric (functions) and interface (ADTs, existential types, APIs, protocols, object interfaces). You use the word in what seems, to me, a very non-standard way. I would like to see the source for your definition.