The starting point of object-oriented programming is to provide a more faithful implementation of the notion of ``type'' in programming. The programming languages we are familiar with come with standard built-in types that we can assign to variables and values:
For example, in C we can use int, float, double, char, .... In Haskell, similarly, we can use Int, Float, Char, Bool, ....
In addition, programming languages provide a way to assign a single name to a collection of values of the same type. The nature of this collection is determined by the underlying architecture assumed by the programming language. Since C reflects the standard von Neumann stored program architecture, we have arrays that provide ``random'' (i.e. equal time) access to each element of the collection, while Haskell, whose semantics is defined with respect to an idealized functional programming architecture, provides us with lists, which have ``sequential access'' (getting to the th item takes time proportional to ) but which come with a number of useful decomposition functions that make inductive definitions easy to implement.
However, no programming language can expect to predefine all the useful ``types'' that we might need in a particular application--we might want to work with stacks or binary search trees or directed acyclic graphs or ...Typically, we define these in terms of the existing primitives available. For instance, in Haskell we might represent a stack as a list or, for efficiency, as a pair of lists, while in C we might represent a stack as an array or, if we want to let the stack grow arbitrarily large, as a linked list.
Regardless of the implementation, we expect a stack to be used in a particular way. Additions to the stack are done using a function ``push'', deletions by a function ``pop'' and the only enquiry that we can make about the state of the stack is the question ``is-the-stack-empty''. Suppose the stack is defined as
int s[100], top_of_stack = 0;
in C and the stack grows as s[0], s[1], .... When the
stack has 10 elements (i.e.,
top_of_stack == 10
) we do not want
a function to access, say, s[7].
In a language like C, this ``integrity'' of the datatype stack is difficult to enforce. It can, in general, be achieved only through disciplined programming--there is no way to guarantee that a stack is never misused.
The preceding discussion on stacks applies equally to other kinds of complex datatypes like binary search trees, directed acyclic graphs etc. In the jargon of programming language theory, these are all examples of Abstract Data Types--data structures that have certain fixed functions to manipulate them and these functions are expected to be rigidly adhered to as the only means of accessing the underlying data, regardless of the implementation.