# What are the main advantages of using declarative programming languages for building AI?

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?

The 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 nth 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.

import qualified Data.Set as Set
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.