>If you separate functions and data, do you keep related data in clusters, or do you keep it as primitives?

The data exists in primitives. And yes you are correct, you eventually need to group this data, but the data must exist in both forms, both as a primitive and both organized. But when you start out building your program you start with primitives at the root and you build up the organization. Allow me to elucidate.

Think of the way your program is organized. You have layers of logic from the lowest level that's primitive to the highest level that's organized and closest to the user.

Let's say you build an App that's a phonebook and prints the addresses of people. You can break the data into two primitives at the lowest later: Person and Address. Then in the next layer you compose Person and Address into PersonWithAddress. Much more organized, but then let's say Companies are added to your design. Now all you have to do is add a company primitive to your design and create a composition called CompanyWithAddress at the next layer of logic.

If you decided to group your primitives immediately and define a Person with address attributes included, you would get design problems later on. Think about what happens when the company is introduced but no Address primitive exists. You're either going to create the Address primitive and have the Person data structure forever keep redundant attributes, or you have redundant address attributes in Company. How would you then define a function FindAllNeighboringAddresses when addresses exists in a fractured/redundant state in your program?

Obviously this is a trivial example, but the same type of problem happens all the time in a more complicated way on projects much more complex than this. When a team encounters such a problem they either add more technical debt, or rewrite a huge portion of the program, or rewrite everything. Choosing the right primitives are a critical part of design.

The entire purpose of primitives is for Maximum flexibility so that you can organize your program to do whatever you want it to do. Objects as a primitive will lock your organization into a specific purpose way to early and often incorrectly. Objects are a bad primitive for this and other reasons.

>You object that mathematics doesn't group things like this. Well, programming is not a purely mathematical activity - it's an engineering one. So even if your observation is accurate, it is not all that relevant.

This is a philosophical argument. Either way historically speaking the foundations of all of software and programming comes from the field of mathematics. It is formally defined both algorithmically and computationally from lambda calculus, von neumann machines to decidability. One can argue that the entire field is a sub field of mathematics.

Additionally, if the definition of mathematics is simply the creation of axioms and deriving theorems and statements from a set of axioms, than programing is the Exact same thing. You are literally assigning values to things and deriving new values (theorems) from your initial set of values (axioms).

Unlike other fields labeled with "engineering" the output of a computer is highly, highly deterministic. That is why also unlike other engineering fields, programming is the subject of formal mathematical analysis more-so than almost every other engineering field out there.