3 ms·
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 Pro
by zmonx 9y ago
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 relations between numeric expressions.
- miguelrochefort 9y agoThis is awesome. Why aren't we all programming this way?
- zmonx 9y agoI am already doing it. Ready when you are!
- PaulHoule 9y agoIt'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.
- zmonx 9y agoWith "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 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.