Simply put, an ADT (Abstract Data Type) is more of a logical description, while a Data Structure is concrete.
Think of an ADT as a picture of the data and the operations to manipulate and change it.
A Data Structure is the the real, concrete thing. It can be implemented and used within an algorithm.
Why is programming not like this?
Why do you think that? Actually, programming is like that. Of course, you can't do that in assembler. Therefore we invented languages that can be more similiar to the human thoughts.
Good programmers program alike examples you gave. But it is not as easy. First you have to understand, what the requirement is. This is hard enough already and most people can't elaborate correctly what they need - therefore a good software designer needs to actually understand what people want, not what they tell.
After that is done you need to see the concepts behind the real world things. That is quite philosophical and needs much experience. And then, after this is done, you have to translate these results into a language of your choice, working around the incapabilities that every language has and choose the correct abstraction level (you don't want to overengeneer anything right?).
Not all people are good at these steps or even aware of them. Even in small examples one can see that it is not trivial. Let's take your display_fibonacci_numbers
function and see what we can improve there (even though its not codereview.stackexchange here).
void display_fibonacci_numbers(
The naming is actually not very precise because you are very specified in the kind of numbers you will display right? That should be described by the function name.
int number "how many fibonacci numbers to calculate"):
Why is it an int
? That is waaay to concrete and even wrong, because ints can of course have negative values right? So what you might really want is a type that can be used for calculating the fibonacci numbers. Maybe a natural number
?
Sequence<int> numbers
while number>0:
numbers.append(calculate_next_fibonacci_number())
number--
Does that really describe what you intend to do? In natural language (which is often a good approximation) we would say "create a list of the specified number of fibonacci numbers". I can't read anything about decrementing a number here, I can't read anything about a static 0
value here. Also nothing about a "calculate_next
_fibonacci_number()" function.
Therefore, in Scala I would e.g. write the code as List.fill(number)(randomFibonacci())
It does not read exactly as in natural language, but it at least contains the same information.
I wanted to give you these examples to show you, that it is hard to write software that way. You need to have experiences, spent good amounts of time to think about things, be able to find the correct concepts and abstractions and THEN have a language that does not hinder you to express them in a concise way.
This is, why not many people program like that. And even worse: humans get used to things. So if one always programs in a language that hinders him in doing the last step, he will maybe continue to program in a new language as in the old one.
Best Answer
This is just a gist on the subject, I strongly suggest following the links herein.
"An abstract data type defines a class of abstract objects which is completely characterized by the operations available on those objects. This means that an abstract data type can be defined by defining the characterizing operations for that type." Liskov, Zilles: Programming with abstract data types
Given the informal definition above, the following pseudo-code can be seen as an ADT:
One point is that nothing is known about stack objects, except what can be inferred by the operations available for it -- in the case above,
stack_create
,stack_push
andstack_pop
. Every single bit of implementation and structure detail is left to the implementation side.Now, take an OO/python class called
Stack
with the methodspush
andpop
.If its clients know that there is a class called
Stack
and they have access to it, one way to see it is that the objects are characterized by the fact that they are instances of the classStack
whose structure containspush
andpop
fields -- regardless of the fact that they are callable. Clients also know that they can inherit fromStack
to create something else and so on.That is, much more is known/exposed to clients. Therefore, this
Stack
type is much less abstract than the previous one (it is exposed as a concrete structure with two fields, has a class with which you can do inheritance, etc). Consequently, one could argue thisStack
type is not an Abstract Data Type conforming to the given definition. To be more strict, one would have to write something like this:And here is a possible implementation of it (in the case below, if you already have the class above and just want to make it an ADT, it is just a wrapper):
Now the client only knows the operations and their interfaces and use them (and only them) to infer what is the type "stack". From outside, nothing is known about the actual implementation and structure of a stack (unless they breach the abstraction by inspecting the structure of the result of
create_stack
).Similarly, in C, you could mimic this by putting these function declarations on a
.h
header file (using a forward declaration standing as the stack type, or simplyvoid*
), and then, putting the struct definition and function implementations in the.c
file.Note on type systems: All the above, of course, are not in the context of languages with static type checking and good type systems -- such language may need to provide certain instruments to make reasonable ADTs possible. Furthermore, the way this theme is exposed in the paper cited above, "strong" type checking appears to be a matter of author preference alone; I did not see them being discussed or shown to be a requirement of ADTs -- the text actually seems to be careful in discussing type checking as something related but not essential. Plus, the explanation of a "C approach to ADT" (see above) was given by William Cook, the researcher who wrote Object-Oriented Programming Versus Abstract Data Types. [edit] On the other hand, Cook writes:
"Abstract data types depend upon a static type system to enforce type abstraction. It is not an accident that dynamic languages use objects instead of user-deļ¬ned abstract data types. Dynamic languages typically support built-in abstract data types for primitive types; the type abstraction here is enforced by the runtime system."