Hacker News new | ask | show | jobs
by anton-107 1788 days ago
Someone in this thread gave an example of casting floats to integers being glue code. But how exactly you want to cast - using round, floor or ceil is a business logic. Where is the meaningful difference in this example?
4 comments

It depends on the intention.

If you're intentionally rounding a number because it's in some algorithm, or maybe your tax law, or to give discounts to customers, then it's not glue code. Using a cast to round down will obscure the intention, but I'd argue it's not really glue code.

However, if you cast the float because you have two systems and the second one only accepts integers, then you are using glue code to make the two incompatible things work together.

The lines a bit blurry sometimes.

But the distinction is still helpful, because less code can potentially (but not necessarily) mean less bugs, less time needed to understand the system, less cost, more performance, etc. And glue code is code that you can potentially (but not necessarily) remove.

Let's say glue code is information preserving.

float to int destroys information... unless the first program uses a float to store integers, so that that information is not destroyed. Similarly, the values could all be something-point-5 (1.5, 2.5, etx). That is, the literal type may be a float, but the application type is narrower than that. Like non-significant white-space, or a set whose elements are listed in an order, but that order is not significant.

A similar view is that, even if the application type for the first program really is a float, and the non-integer part is signicant to that program, that the type for this specific transfer is an integer. The specific way it's done depends on what's needed.

Is this "business logic"? Maybe. I would tend to say business logic requires some computation, other than a lossless transformation. Putting it into a different form, that is exactly equivalent, I would call glue.

I don't believe there is any authoritative definition; all we can do is note distinctions.

Perhaps I'm just playing semantics with a "transfer type"... but I think there is something important and useful in this distinction.

I'm not sure about that. But business logic is what you have to do de Novo to make your product. I work for an invoice processing company. Connecting to a/many Saas is glue code (this is most of our codebase). The code that decides the rules for how an invoice is "approved" by reviewers is business logic.
Is the choice meaningful? Or just something you have to type out?

In the casting example, it's meaningful: there are several ways to "round" a float, each having different consequences. As a programmer, you have to make a choice driven by some business requirements, and encode it (preferably only once for a given business concept).

Sometimes, however, you can modify the source of the float - push the decision upstream, so that your code always gets an int the way you like. This is one way of reducing glue code, but it's not always available.

Anyway, a different type of glue code - I think the most common one, one I call "code bureaucracy" - is reconciling concepts between systems/modules. One system produces data that's almost, but not quite, like what the other system wants to consume. Sometimes it's literally the same data, but named as two separate concepts with separate ownership. This kind of glue code can grow in size rapidly as a consequence of following seemingly good design practices, and often feels pointlessly wasteful.

Imagine you're writing a 3D video game. You decide to use a library to load 3D models. You give it a data stream, and get back a list of (x, y, z) points. Unfortunately, despite containing exactly the same data, perhaps in exactly the same memory layout, that list of points is not a proper "list of points" as the rest of your game logic understands it. You're faced with 3 options:

1. Change your game to use library's format for representing 3D data. You're reluctant, because it would tie your system to the API of a third-party component. It would also be difficult if you used another library (perhaps for physics handling) that used its own "list of points" representation.

2. Change the library to use your game's format. Not the work you wanted to do, and the library author won't be interested in it anyway.

3. Write glue code to convert between the formats. Can be as simple as switching out typesystem labels, or as complex as translating each point in a loop.

For obvious reasons, everyone will choose option 3. What's curious is, though, that the reasons are more social than technological. You don't want 1. because it will make you dependent on a third-party system. That system's author won't accept 2., because it'll make them dependent on you. Hence, option 3.

The exact same thing happens internally, within subsystems developed by different people, and even with code of a single author, across abstraction boundaries. It's an ironic example of Conway's law: system design reflects social structures designers live in. After all, there is another option - one that is rarely thought about, one that's sometimes considered a violation of good design - one that's socially expensive:

4. Get together with the other system's author (and possibly with other users) and agree on a common representation you'll both use.

It breaks abstraction layers. It breaks ownership separation. It breaks responsibility division.

It also eliminates hell of a lot of glue code.

4. Get together with the other system's author (and possibly with other users) and agree on a common representation you'll both use.

Good standards are a wonderful thing, and IMHO we as an industry tend to invest far too little in establishing them now.

I believe programming languages can help a lot here, if you take the view that their standard library should be not just a ready-made toolbox but also a library of standards, in the sense of establishing concepts and (depending on the language) terminology, interfaces, types, symbolic constants and other common foundations for implementations in a specific field.

An example of not doing this is JavaScript, where the paucity of standard data structures and algorithms out of the box leads to huge trees of almost-trivial dependencies and to a culture where almost any data we need to move around just gets shoehorned into objects or transmitted as JSON.

A more positive example is Haskell, where the standard typeclasses capture several broadly useful programming patterns and given them a widely recognisable name and interface. Haskell libraries (standard or otherwise) can then provide algorithms with generic interfaces defined in those terms that are not tied to specific implementations or specific data structures. This results in an ecosystem of data structures and algorithms that are relatively easy to compose even when coming from different sources, which often allows Haskell programmers to express ideas very concisely. A related example is the use of generic programming within the standard library of C++, particularly with the arrival of concepts in C++20 that should serve a similar role to typeclasses in Haskell.

Both of those languages also learned some of these lessons the hard way. Each of them failed to establish a single, standard way to represent something as “simple” as a text string, with the result that many popular libraries did things their own way, and passing text around between things like databases, UIs and remote communications protocols can be a chore to this day.

I think it’s interesting that in some cases, the community around a language has evolved a kind of de facto standard interface for common tasks like accessing a SQL database, so that for example whether you’re talking to Postgres or Maria or SQLite or whatever, the corresponding drivers all provide very similar interfaces and are (mostly) interchangeable as far as calling code is concerned. I think it would be helpful if we did a lot more of that as an industry, within and perhaps even across languages, so we don’t keep reinventing the wheel every time we need to access a relational database or to draw a simple GUI or transmit data using typical Internet protocols or represent mathematical structures beyond the very basic ones.