# Invariants —JS: Building abstractions with data

Abstraction allows us to avoid thinking about the implementation details and focus on usage. Moreover, if necessary, you can always rewrite the abstraction implementation without stressing about breaking the code that uses it. But there's another important reason to use abstraction, finding invariants.

An invariant in programming is a logical expression that defines the consistency of a state or data set.

Let's look at an example. When we supplied the constructor and selectors for rational numbers, we implicitly had the following invariants:

``````const num = makeRational(numer, denom);
numer === getNumer(num); // true
denom === getDenom(num); // true

// Or considering normalization
// numer / denom === getNumer(num) / getDenom(num);
``````

By passing the numerator and denominator to the rational number constructor, we expect to get the same numbers if we apply the selectors to that rational number. This is how we determine if the abstraction is done correctly. This code is practically a test!

Invariants exist for any operation. And sometimes they're pretty tricky. For example, rational numbers can be compared to each other but not directly because the same fractions can be represented in different ways: 1/2 and 2/4. Code that doesn't take this fact into account won't work correctly:

``````const num1 = makeRational(2, 4);
const num2 = makeRational(8, 16);

console.log(num1 === num2); // false
``````

The action of reducing a fraction is called normalization. This includes several operations, such as shortening a fraction, determining the sign, and moving the sign to the numerator. Normalization can be done in different ways. The most obvious one is to execute it during fraction creation, inside the `makeRational()` function. The other is to perform normalization when accessing the fraction using `getNumer()` and `getDenom()`. The latter method has the disadvantage that normalization happens with every call. This can be avoided by using memoization.

Given the new inputs, it's clear that the invariant linking the constructor and selectors need to be modified. The `getNumer()` and `getDenom()` functions should return the normalized values and not the raw ones.

``````const num = makeRational(10, 20);
getNumer(num); // 1
getDenom(num); // 2
``````

The abstraction not only hides the implementation from us, but it preserves the invariants. Any code without abstraction is fraught with the risk of internal transformations that can remain unnoticed:

``````// Traversing the constructor

// This data is not normalized because the constructor was not used
const num = { numer: 10, denom: 20 };

// Not what it is supposed to return (normalized return is expected):
getNumer(num); // 10
getDenom(num); // 20

// Direct modification

const num = makeRational(10, 20);
// there can be no normalization here, since it's a direct change
num.numer = 40

getNumer(num); // 40
getDenom(num); // 20
``````

I.e., working with data directly and avoiding abstraction can easily break invariants provided by additional logic in the constructor or selectors. So it's important to use the code the way the authors intended.

Looking at the examples above, you may have one genuine question. Is it possible to forbid direct access to data? Generally, yes. This approach is called data hiding. Usually, languages use a special syntax to hide data. However, data hiding can be done without using special tools: it requires higher-order functions. This method implies building abstractions with anonymous functions, closures, and message passing (more in SICP). <!---If you want to learn more about this, try our JS: Compound data course.---> ## Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

## For full access to the course you need a professional subscription.

130
courses
1000
exercises
2000+
hours of theory
3200
tests

• 130 courses, 2000+ hours of theory
• 1000 practical tasks in a browser
• 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions    Suggested learning programs
profession
Development of front-end components for web applications
10 months
from scratch
Start at any time

## Use Hexlet to the fullest extent! 