Hacker News new | ask | show | jobs
by formulathree 1060 days ago
There's another style of programming that has much of the required interfaces predefined in the language itself. A set of universal interfaces that conceptually work universally for all programming.

It's mathematical interfaces.

Commutativity, Identity, Associativity, Ordinality and more. For these interfaces, it becomes less about "designing" interfaces with gut intuition, guessing and checking... but more about finding the final design via calculation.

Math is known to be universal so it makes sense why mathematical interfaces have such wide application. When you use mathematical interfaces and compose everything along those parameters... it's no longer about "you never come up with the right interface at first"... That concept becomes less relevant.

It's hard to agree with me when I'm just saying it here. It has to "click" after you tried it with a language that supports this type of programming first class.

5 comments

I have never come across a codebase written in this style that is any good. I think people inclined in this direction usually seem to fail to reify the domain they're working within, preferring to leave everything in terms of the mathematical primitives. This is much harder to work with than systems that have thought through the concrete interfaces useful in their concrete domain. (And sure, implementing those using "mathematical interfaces" is great.)
I actually somewhat agree. I think there's a gradient here between modularity and useability. The more modular something is, the less user friendly it becomes.

The problem the GP is talking about here is a modularity problem right? He designs (aka guesses) the interface and finds out later that his guess was wrong. Mathematical interfaces deal with this problem better.

However mathematical interfaces are less user friendly and less intuitive, especially for someone not familiar with mathematical interfaces.

The reconciling solution is that the public interface can be very domain specific and made narrow in usage. The logic underneath this public interface can remain mathematical and therefore more amenable to future changes.

Yep!

> However mathematical interfaces are less user friendly and less intuitive, especially for someone not familiar with mathematical interfaces.

Actually I think it's less useful for people who are very familiar with the mathematical techniques as well.

Pure math also specializes things by domain. Certainly experienced mathematicians are capable of seeing the generalism underneath the specialization and are able to re-derive it, but to make progress they mostly don't do that, they build on top of the specific "interface".

I hear where you're coming from, but I also have had to operate a poorly written Haskell codebase that used these concepts well and still has poorly written code. I once had similar views, but eventually, I let them go after finding my zeal for functional purity a panacea or a self-salving truism meant to retain my sanity after dealing with monstrosities written in traditional imperative languages.

I think that the sad truth is that ugly code reflects an impedence mismatch between the model in the codebase and the reality of how that software would be used, which wouldn't be obviated by taking a more formal or calculated approach to constructing the code. I've seen bad code written in every language and good code written in most languages. Moving state mutation to edges just lifts and shifts the hard part, it doesn't make it any easier.

But to conclude this dissent with a point of agreement, I that the style of programming you're talking about already exists and is common, is called SQL, and is built on very concrete primitives in set theory. You get ACID properties which give transactional isolation, at-rest data integrity based on normalization and uniqueness constraints and purely functional data transformation that can live in a purely functional language (SQL) rather than inside whatever application level language is chosen. And for what it's worth, I have seen enormous, universal improvements in codebase quality by "lifting and shifting" computation that could more comfortably and ergonomically live inside SQL to occurring there.

To your point, I've seen this occur much more successfully when utilizing an ORM approach that is more functional (query builder flavor) than imperative (object mapper flavor).

> Math is known to be universal

Is this really the case? Or what do you mean by this exactly? Gödel’s incompleteness does apply to it as well, and we can for example only determine the Busy Beaver number up to a fix point no matter what. Though it probably doesn’t matter from a practical perspective, I would just like to know in what sense do you mean universality.

Quite so. I’m currently reading Algebra Driven Design by Smart McGuire. It’s a valuable perspective.
Which languages support mathematical interfaces? Is this similar to functional programming?
What's not mentioned in the comment you're replying to is the postgraduate-level type theory you need to learn and constantly refine to work with these languages at a practical level. So be aware that even if you put the time into learning them, it's unlikely you'll get to use them at work.

Look up Coq, Agda, Lean, and Idris. I would start with Coq, it's the most used. Idris is more like Haskell and programmer-oriented.

Edit: Nevermind, apparently they were just talking about Haskell...

I'm more thinking in terms of logical primitives for the design of modules and components that can be composed, decomposed and recombined. I'm thinking less about proof based correctness.
On some level, all of them do. Programming lives in a grey area between human language and discrete, symbolic logic.

The way in which we get to an "application" is in designing interfaces that look more like the domain, and less like the implementation. If you can define the math you're using symbolically, you can apply it directly to express ideas from, e.g. linear algebra, set theory, graph theory. And libraries exist for all of those things - you can make the interface more convenient with additional syntax and compiler assistance, and you can frame the program in terms of theorem-proving logic(which is the realm of stuff like Coq) which provides an extra degree of assurance that the program does the thing you defined it to do by adding more detail to that definition, but often the problem requirements fit in the realm of "just tell the computer to do things" - and so imperative code is the default, everywhere.

But we can also take concepts like "name", "job", "age", "ethnicity", "gender", and enter them into a computer. And all of those are human ideas, socially constructed and philosophical in some degree. Mathematics doesn't help us express the essence of those ideas, it just tells us of ways to symbolize the tokens involved, which can be made relatively general and flexible but all of which ultimately stem from a predesigned enumeration of options like the codepoints available in UTF-8 or the range of values in a 32-bit integer.

And a lot of the mathematical stuff is subsumed by the social/philosophical in practical application: we agree that the data has some kind of truth to inform us, because it's compatible with our framework for understanding it. And if you have a setup that fits the model of computing, something like taking a sensor that emits numeric values at a regular frequency and processing the output into some kind of signal - then you can program mathematically all throughout. But if you're mostly dealing with human language, you're constantly hammered with interface problems for other reasons.

Human concepts can be placed under mathematical interfaces.

Inversion for gender

   ~male = female
Ordinality for human hierarchies:

   CEO > manager > worker
Commutativity for human action:

   Punch human + kick human = damaged human
   Kick human + punch human = damaged human
Mathematical interfaces are different from mathematical primitives which I believe you have mistakenly combined into a singular concept in your response.

By fitting human concepts into mathematical interfaces you develop a sort of algebra dsl for the language allowing you to apply all mathematical theorems of the equivalent algebra to the domain. Those theorems are the generalities that improve design by improving modularity.

Suddenly for ordinal concepts I can use a general min or max function across all domains. By using mathematical interfaces I am in the realm of the ultimate generality. Normally people would be writing some form of equivalent logic to derive the lowest ranking human in a hierarchy when really the concept of min covers it.

These basic mathematical interfaces that apply to basic numerical logic are found to be expandable across domains. There's no proof or logic as to why these interfaces happen to be more universal. It's just a gut feeling after using this interfaces more that they happen to be extremely universal. Thus there's no way I can prove to you what I'm saying is correct, you ultimately have to try it.

The language of math itself is functional. In fact "functional programming" is basically programming as if everything was math. Think about it, does math allow for variable mutation inside a mathematical expression? No.

Interfaces that mutate internal values do not exist in mathematics. So, in essence, yes. Mathematical interfaces only support functional operations.

Haskell would be the language.

Is this really true? Have you written idiomatic Haskell before? Not trying to question your familiarity if so, but a large part of doing so is utilizing interfaces that do just that.

One of the most critical parts necessary to fully grok idiomatic Haskell is how it uses mathematical interfaces, specifically category theoretic interfaces to structure internal state mutations -- specifically Monads and other structures. This specific interface utilizes two properties, identity and associativity, to do this and create one-way, sequenced computations still couched in formal rigor and laws.

Contrasted to other languages, in order to do any useful I/O in Haskell, one needs to understand and use Monads. Of course, the utility of the Monad includes but is not limited to just this use case.