Ask HN: Reversible function definitions

5 points by posterboy ↗ HN
What do computer scientists call a function that allows inference of transformations between two data types from a single definition, and does any language support this and explicit calls or implicit conversions?

   function convert
   (rad_t) phi = pi/180 * (deg_t) phi
   rad_t alpha = convert((deg_t) 45)  //explicit
   deg_t beta = pi/4 + alpha          //implicit
rad_t and deg_t might be together in a typeclass archs or sumsuch.

disc.: I can't reach stackoverflow, so I'm posting here.

9 comments

[ 2.9 ms ] story [ 31.3 ms ] thread
There are Python packages that do what you want (units and numericalunits), but they aren't used much presumably due to the overhead they bring. You might also be interested in Prolog which feature what is, essentially, invertible functions. E.g:

convert(Deg, Rad) :- nonvar(Deg), Rad is Deg * pi / 180. convert(Deg, Rad) :- nonvar(Rad), Deg is Rad * 180 / pi.

In this case, it doesn't work well because of floating point math, but it works well for functions manipulating lists. The Factor language also feature function inversion in an addon: http://docs.factorcode.org/content/article-inverse%2Cintro.h...

Afaik, figuring out what functions in general are invertible is an unsolved problem in computer science.

They're call isomorphic functions.

Recently, I've been thinking a lot about them and also have been looking for a language/framework that supports them. What would be very interesting is a way to inverse non-isomorphic functions and get a constrained set of possible values (i.e., the reverse of age(birth, now) would return a range of date for either birth or now). Often, the complete value is not necessary and you're only interested in some subset of it (i.e., that a person's age is greater than 18).

It is true: An isomorphic function admits an inverse function.

However, what you are describing in the second paragraph is a generalization of functions: You are now reasoning in terms of relations, where a single entity can be related to one or more others. Hence, a programming language that reasons in terms of relations (not functions) seems very appropriate for your use case. Logic programming is a such a natural generalization of functional programming.

For example, if you want to reason flexibly over dates and times, check out Prolog and its finite domain constraints.

Michael Hendricks has implemented a great library for this, called library(julian):

http://mndrix.github.io/julian/

This library lets you reason in terms of ranges and other constraints to describe general relations between dates and times.

There is a natural generalization of functions, called relations. To benefit from relations, have a look at a logic programming language such as Prolog. In Prolog, relations are defined by predicates and constitute the basic building blocks of all Prolog programs.

A predicate is indeed sometimes called "reversible" if it can also be used in a direction that seems somewhat unexpected at first. For example, the predicate length/2 can be used to both compute the length of a list and generate a list of a given length.

However, a predicate should ideally not only be "reversible", but indeed completely general. This means that we should also be able to ask: Which solutions are there at all? This is a query where nothing is given, and the Prolog system must find solutions on its own. Indeed, length/2 also satisfies this criterion. We can ask for example:

   ?- length(Ls, L).
   Ls = [],
   L = 0 ;
   Ls = [_2168],
   L = 1 ;
   Ls = [_2168, _2174],
   L = 2 ;
   etc.
and the Prolog system generates as many lists (and their lengths) as we want.

So, calling such predicates "reversible" still does not do justice to what they actually are, namely completely general relations that work in all directions. Therefore, when programming in Prolog, do not fall into the trap of calling your predicates "reversible" or "usable in the other direction" is if there were only one such other direction. Instead, think in terms of relations between entities, and use language features that allow such general reasoning.

For instance, let us consider your concrete example. Using Prolog and its CLP(R) constraints, we can very generally describe the relation between floating point numbers. CLP(R) is for example available in SICStus Prolog and also a few other systems:

    :- use_module(library(clpr)).

    pi(Pi) :- Pi is 4*atan(1).

    rad_deg(Rad, Deg) :-
            pi(Pi),
            { Rad = Pi / 180 * Deg }.
Now the point: rad_deg/2 is a general relation and can be used in all directions.

For example, it can be used to compute the first argument if only the second is known:

  ?- rad_deg(Alpha, 45).
  Alpha = 0.7853981633974483 .
Conversely, it can be used to compute the second argument if only the first is known:

  ?- rad_deg(0.7853981633974483, Beta).
  Beta = 45.0 .
Here is your slightly more complex example, which is also an instance of computing the second argument:

   ?- pi(Pi),
      rad_deg(Alpha0, 45),
      { Alpha = Pi/4 + Alpha0 },
      rad_deg(Alpha, Beta).
   ...,
   Beta = 90.0 .
But it does not stop there! The relation can also be used in the most general way to answer: Which solutions are there at all?

  ?- rad_deg(R, D).
  {D=57.29577951308232*R}.
In this case, it provides an answer that expresses the general relation between the two arguments even though none of them are known.

And it still doesn't stop there! The relation can also be used to ask: Does the relation hold between two given numbers?

  ?- rad_deg(0, 0).
  true.
If the relation doesn't hold between two given floating point numbers, the system tells us:

  ?- rad_deg(0, 1).
  false.
So, this is a true relation between floating point numbers, usable in all directions!

Caveat: Floating point numbers are an extremely bad way to represent numbers, and I can only advice to use better representations instead. For example, Unum computing looks very promising. In Prolog, consider using rational numbers, available via CLP(Q). Declarative reasoning over integers is available via CLP(FD) constraints. All these systems let you implement general

This is awesome. Why aren't we all programming this way?
I am already doing it.

Ready when you are!

It's an interesting question.

In the 1980s, there was a lot of interest in Prolog, particularly people thought it would be good for massively parallel programming since you don't have to specify the exact sequence things happen in.

It was discovered pretty early that Prolog does not parallelize well, so that research went in another direction towards languages that were less expressive such as KL1.

I think Prolog has not really caught on because the combination of imperative in logic programming is awkward. For instance, you might implement a loop that uses logical failure to backtrack (and run the next iteration of the loop) when you have what you think of as a true condition.

Since then there has been a lot of interest in Datalog which is not a precisely-defined language with a particular syntax, but rather the pure logical core of Prolog which can be implemented in languages like Clojure or the SPARQL-RDF ecosystem, etc.

On the other hand there are also "constraint programming" languages which are more expressive than Prolog as well as SAT/SMT solvers and other tools that use more complex solving and optimization strategies.

With "constraint programming" languages, do you mean languages such as MiniZinc that provide dedicated syntax for some tasks?

I am asking because support for constraint programming is among the most important and most distinguishing features of common Prolog systems: All widely used Prolog systems (GNU-Prolog, SICStus etc.) ship at least with a constraint solver over integers, and often with many more, such as constraints over Herbrand terms (dif/2), constraints over rational numbers and also constraints over Boolean variables (SAT constraints).

So, support for constraint programming is typically provided also natively by Prolog systems, with a simple and also very expressive syntax that blends in seamlessly into Prolog like any other relation.

There was a recent HN discussion about why Prolog is not yet more popular:

https://news.ycombinator.com/item?id=14439137

In my view, Prolog implementations are now becoming interesting because they are now quite robust, feature-rich and also reasonably efficient.

I actually started that thread about Prolog :)

Recently, I've been looking at Mercury and Eve. I really thing logic programming is underrated.