10.5. Functional Immutable

  • Purely functional data structures have persistence

  • (keeps previous versions of the data structure unmodified)

The array with constant access and update times is a basic component of most imperative languages, and many imperative data-structures, such as the hash table and binary heap, are based on arrays. Arrays can be replaced by maps or random access lists, which admit purely functional implementation, but have logarithmic access and update times. Source: [1]

Variables are immutable, i.e., it isn't possible to modify one once it has been initialized. However, we can create a new variable. The immutable nature of variables helps preserve the state throughout the program. [2]

Mutable:

>>> data = [1, 2, 3]
>>> id(data)  
4505485888
>>>
>>> data += [4, 5, 6]
>>> id(data)  
4505485888

Immutable:

>>> data = (1, 2, 3)
>>> id(data)  
4506214016
>>>
>>> data += (4, 5, 6)
>>> id(data)  
4502812416

10.5.1. Mutable vs Immutable

../../_images/functional-immutable-tuple.png
../../_images/functional-immutable-str.png
../../_images/functional-immutable-tables.png

10.5.2. Changes

../../_images/functional-immutable-sharedstate.png

10.5.3. Immutable Types

  • int

  • float

  • complex

  • bool

  • None

  • str

  • bytes

  • tuple

  • frozenset

  • mappingproxy

10.5.4. Mutable Types

  • list

  • set

  • dict

10.5.5. Comparison

Table 10.1. Comparison

Immutable

Mutable

int

float

complex

bool

None

str

bytes

bytearray

tuple

list

frozenset

set

mappingproxy

dict

NamedTuple

namedtuple

array

TypedDict

dataclass(frozen=True)

dataclass(frozen=False)

10.5.6. Array

Return a new array whose items are restricted by typecode, and initialized from the optional initializer value, which must be a list, string or iterable over elements of the appropriate type.

Arrays represent basic values and behave very much like lists, except the type of objects stored in them is constrained. The type is specified at object creation time by using a type code, which is a single character. The following type codes are defined:

Table 10.2. Comparison

Type code

C Type

Minimum size in bytes

'b'

signed integer

1

'B'

unsigned integer

1

'u'

Unicode character

2 (see note)

'h'

signed integer

2

'H'

unsigned integer

2

'i'

signed integer

2

'I'

unsigned integer

2

'l'

signed integer

4

'L'

unsigned integer

4

'q'

signed integer

8 (see note)

'Q'

unsigned integer

8 (see note)

'f'

floating point

4

'd'

floating point

8

SetUp:

>>> from array import array

Define:

>>> data = array('b')  # 8 bit signed integer -> values from -128 to 127

Use:

>>> data.append(0)
>>> data.append(1)
>>> data.append(127)
>>> data.append(128)
Traceback (most recent call last):
OverflowError: signed char is greater than maximum
>>> data.append(-1)
>>> data.append(-128)
>>> data.append(-129)
Traceback (most recent call last):
OverflowError: signed char is less than minimum

10.5.7. Mutable Dataclass

>>> from dataclasses import dataclass
>>> @dataclass
... class Point:
...     x: int
...     y: int
>>> pt = Point(x=1, y=2)
>>> pt.x = 10
>>> pt.y = 20
>>> pt
Point(x=10, y=20)
>>>
>>> pt.z = 30
>>> pt
Point(x=10, y=20)
>>>
>>> vars(pt)
{'x': 10, 'y': 20, 'z': 30}

10.5.8. Immutable Dataclass

>>> from dataclasses import dataclass
>>> @dataclass(frozen=True)
... class Point:
...     x: int
...     y: int
>>> pt = Point(x=1, y=2)
>>> pt.x = 10
Traceback (most recent call last):
dataclasses.FrozenInstanceError: cannot assign to field 'x'
>>>
>>> pt.x = 20
Traceback (most recent call last):
dataclasses.FrozenInstanceError: cannot assign to field 'x'
>>>
>>> pt
Point(x=1, y=2)
>>>
>>> pt.z = 30
Traceback (most recent call last):
dataclasses.FrozenInstanceError: cannot assign to field 'z'
>>>
>>> pt
Point(x=1, y=2)

10.5.9. References