# Data abstraction

Data abstraction is an abstraction methodology in which functionality is separated from representation—we don't need to know how data is represented internally; we just need to know what it does.

Data abstraction is done by defining an abstract data type (ADT), which is a collection of constructors and selectors. Constructors create an object, bundling together different pieces of information, while selectors extract individual pieces of information from the object.

## Contents

ADTs can be implemented by functions. Functional ADTs are usually immutable; that is, their fields cannot be modified.

### Examples

```empty_rlist = None

# constructor
def make_rlist(first, rest=empty_rlist):
return first, rest

# selector
def first(r):
return r[0]

# selector
def rest(r):
return r[1]```

```# constructor
def make_point(x, y):
return x, y

# selector
def x_coord(point):
return point[0]

# selector
def y_coord(point):
return point[1]```
and a function that uses it:
```def slope(point1, point2):
"""Return the slope of the line that connects POINT1 and POINT2."""
return (y_coord(point1) - y_coord(point2)) / (x_coord(point1) - x_coord(point2))```

An ADT for rational numbers (in dispatch function style) that supports mutation:

```# constructor
def rational(x, y):
"""
>>> rat = rational(1, 3)
>>> print_rational(rat)
1 / 3
>>> rat[0]('numer', 2)
>>> print_rational(rat)
2 / 3
"""
# mutator
def put(field, value):
nonlocal x, y
if field == 'numer':
x = value
elif field == 'denom':
y = value

# selector
def get(field):
if field == 'numer':
return x
elif field == 'denom':
return y
return put, get

def print_rational(rat):
print("{0} / {1}".format(rat[1]('numer'), rat[1]('denom')))```

ADTs can also be implemented through object-oriented programming as Python objects. OOP ADTs are mutable and have built-in constructors (`__init__`) and selectors (dot notation).

### Example

An ADT for a bank account:

```class Rlist:
class EmptyList:
pass

empty = EmptyList()

# constructor
def __init__(self, first, rest=empty):
self.first = first
self.rest = rest```

## Data abstraction violation

A data abstraction violation occurs when you assume some representation of the ADT, bypassing the constructors and selectors. For example, we are committing a data abstraction violation when we write the following `slope` function using the point ADT above:

```def slope(point1, point2):
"""Return the slope of the line that connects POINT1 and POINT2."""
return (point1[1] - point2[1]) / (point1[0] - point2[0])```
We assume that the internal representation of a point is some type of sequence.

Data abstraction violations destroy flexibility because if you want to change the representation of the ADT, you must change all the functions that use the ADT. If you maintain data abstraction, you need only change the constructors and selectors.