What specific advantages of declarative languages make them more applicable to AI than imperative languages?
What can declarative languages do easily that other languages styles find difficult for this kind of problem?
Artificial Intelligence Stack Exchange is a question and answer site for people interested in conceptual questions about life and challenges in a world where "cognitive" functions can be mimicked in purely digital environment. It only takes a minute to sign up.
Sign up to join this communityThe advantage of a declarative language like Prolog is that it can be used to express facts and inference rules separately from control flow.
This allows the developer to focus on the data and inference rules (the knowledge model), and allows the developer to extend the knowledge model more easily.
I should add that in practice, this dichotomy between facts/rules on the one hand, and control flow on the other, is not strict. A knowledge engineer who writes a code base in Prolog does sometimes have to consider control flow. The "!" operator is used so that the developer can influence the evaluation of the rules.
There's no objective reason to state that declarative languages are better suited for AI development. However, there's indeed a bias towards them in practice. Although most functional languages are impure (that is, they allow side effects), and such can't count as "declarative", a few languages are purely functional (that is, they don't allow side effects), most prominently Haskell. Purity is key here. In Haskell, even I/O is pure.
The key difference between imperative languages and (purely) functional languages is in the way they describe the program. An imperative program describes how to do stuff, that is, algorithms. It specifies the specific instructions that the machine must carry on in order to perform the computation. OTOH, purely functional languages describe what is to be computed, that is, the relationship between the input and the output. In mathematics, "function" is just a fancy name for a relationship between an input and an output.
Again, in the mathematical sense, the only variability is that of the function's arguments. That is, the function's output depends solely on its input (arguments). This is known as referential transparency. Referential transparency states that:
$$ \forall f \in \varphi, \forall x \in \delta_f, fx = fx $$
Where $\varphi$ is the set of all functions, and $\delta_f$ is $f$'s domain. For the typical imperative language's definition of "function", the above doesn't hold. For instance, C's getchar()
does not always return the same value.
Let's say we want to calculate the set of the ten least prime numbers whose least significant digit is $3$. First, in mathematical notation:
$$ \mathbb{S} = \mathbb{G}_{10} = \{ x \mid x \in \{ p \mid p \in \mathbb{N} \setminus \{0, 1 \} \text{ and } (\forall q \in \mathbb{N} \setminus \{0, 1, p \}, p \text{ mod } q = 0) \}, x \text{ mod } 10 = 3 \} $$
Where G(n, s)
is the set of the lesser n
th elements from s
. In mathematics, you don't worry about how is the set $\mathbb{S}$ supposed to be computed, but rather about $\mathbb{S}$'s definition itself.
Now, in Python (in imperative style):
def is_prime(n):
for x in range(2, n):
if n % x == 0:
return False
return True
def foo():
s = set()
n = 2
while len(s) < 10:
if is_prime(n) and n % 10 == 3:
s.append(n)
return set(s)
In Python, we care about (and are responsible for) the algorithm being used to compute the set. We specify, pretty much in recipe-style, how to build the set from scratch. If there's an algorithm that may be better suited for checking whether a number is prime or odd, but we don't use it, it's our fault, not Python's.
Finally, Haskell steps in:
import qualified Data.Set as Set
isPrime :: Integer -> Bool
isPrime n = ( == 1 ) . length . filter ( == 0 ) . map ( n `mod` ) $ [ 2 .. n ]
s = Set.fromList . take 10 . filter ( ( == 3 ) . ( `mod` 10 ) ) . filter isPrime $ [1..]
Haskell's version is a lot more like the mathematical model than Python is. We define the isPrime
function in terms of the constraints that a prime number must obey, not by describing a step-by-step algorithm to do such a check. Moreover, s
(the set we have been defining so far) is defined in terms of the constraints its members must obey, rather than in terms of an algorithm to compute s
itself. The compiler, more often than not The Glorious Glasgow Haskell Compilation System (a.k.a GHC), is the one responsible for generating an algorithm. GHC's optimizer is known to be one of the strongest in the world, not because its the best compiler of 'em all, but because Haskell's nature allows for this.
Haskell (and other functional languages), in summary, have several features that make it look, taste, and behave like pure math:
Referential transparency and purity.
A very strong type system, with such exotic (but very useful!) stuff as recursive (and algebraic) data types.
So, the bottom line is that AI researchers often prefer functional languages over imperative languages (or, more assertively, pure over impure languages), because they are attempting to define artificial intelligence itself, by means of functions (relationship between an input and an output). At the end, we do this because we have no real algorithm for human-level intelligence to raise from a handful of transistors. Also, there's been a historical bias towards these kind of languages, starting with John McCarthy, Lisp's creator and a pioneer in early AI research.