How not to put together a set of keys and values ​​for a given set of maps?

The official Clojure doc states:

Most systems for defining structures combine a specification of a set of keys (for example, keys on a map, fields in an object) using the specification of the values ​​indicated by these keys. That is, in this approach to the scheme for the map, they can say: the type of a-keys is x-type and: the type of b-keys is y-type. This is the main source of rigidity and redundancy.

And in this SO question: clojure.spec human readable form?

The following example is given:

(s/def ::car (s/keys :req [::tires ::chassis]))

(s/def ::tires (s/coll-of ::tire :count 4))

(s/def ::tire (s/or :goodyear ::goodyear}
                    :michelin ::michelin))

      

My question is: how is this not ridig and redundant? Contrary to what would be an example (in Java?) Of something rigid and redundant?

As I see it, you still cannot define a car that would be, say, a dragster with six wheels, because it ::tires

must have 4 elements. You cannot define a floating car whose rear wheels will be propellers.

How does the above example differ from, say, static typing in terms of rigidity and redundancy? How is this different from a Java class car

that will be instantiated using an instance tires

containing four instances tire

?

Basically, I think I don't understand that you are looking at the map telling what keys are required. So far, so good. But then the keys have a built-in spec'ed, so the values ​​indicated by these keys are also specified too !? How are things "not merged" here?

+3


source to share


1 answer


Zoom in for a moment and think of software development as a practice. We'll get to the spec at the bottom.

As engineers, one of our most important skills is the ability to define and think in abstractions .

Think about how the composition of functions makes it easier to build complex software. This makes it easier to define a complex function, allowing us to think more broadly about what is going on. The nice advantage of this is that it also allows for smaller functions that add up to large ones that need to be reused when writing similar but slightly different complex functions.

Of course, you don't need functions at all. You can write your entire project in one function. However, this is a bad idea, primarily because it combines the purpose of the function with the specification of the function.



For example, function make-car

calls build-engine

, build-drivetrain

, install-interior

and more. You can of course take the code from each of them and paste them inside make-car

. But as a result, you lost your abstraction . make-car

cannot be improved or changed other than the code itself make-car

. The engine code cannot be upgraded or reused to make any other vehicle. What for? Because the knowledge of how to build this engine for that particular vehicle specification is built into the function make-car

.

So, make-car

shouldn't define how to assemble an engine (or any other car components); it simply defines which components make up the vehicle and how they work together. The specificity of these components does not apply to working knowledge embedded in make-car

.

The comparison with the spec should now be clear:

Likewise, spec allows you to define objects as abstractions. Can you embed knowledge of the object into the spec / schema? Certainly. Can you directly plug the specification of individual components into the entity definition? Yes. But in doing so, you combine the entity with the definition of its components. The loss is the same as with the above: you lose the abstraction of what the object is, because now you have to change the definition of the object to change the details of the entity , which are indeed the details of its component; and you've lost the ability to reuse definitions for similar but different objects.

+2


source







All Articles