The difference is that:
- Functional programming is based on reduction—rewriting complex expressions into irreducible values by using directional rewrite rules, with a strict sense of "input" vs. "output";
- Logic programming is based on constraint satisfaction—finding solutions to sets of statements by searching for values that, when plugged in for the statements' variables, make those statements true.
I.e. in Haskell you can write something like [(i,j) | i <- [1..10], j <- [5..20], j < i]
and Haskell returns a list of all possible values of i
and j
that adhere to j < i
.
But in Haskell the way this works is in terms of evaluating function applications. Your expression...
[(i,j) | i <- [1..10], j <- [5..20], j < i]
...is equivalent to this in Haskell:
concatMap (\i -> concatMap (\j -> if j < i then [(i,j)] else []) [5..20]) [1..10]
Where concatMap
is a standard function that can be defined like this:
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap _ [] = []
concatMap f (a:as) = f a ++ concatMap f as
-- concatMap uses this function as well:
(++) :: [a] -> [a] -> [a]
[] ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)
And in Haskell evaluation, the equations that define the functions are only ever used in a "left to right" direction. To evaluate an expression, we search for occurrences that match the left hand sides of the equations, and substitute the right hand side counterparts—never the other way around. So:
concatMap (\i -> concatMap (\j -> if j < i then [(i,j)] else []) [5..20]) [1..10]
-- Reduce the outermost concatMap with its definition's second equation:
==> concatMap (\j -> if j < 1 then [(1,j)] else []) [5..20]
++ concatMap (\i -> concatMap (\j -> if j < i then [(i,j)] else []) [5..20]) [2..10]
-- Reduce the first `concatMap` with its definition's second equation:
==> if 5 < 1 then [(1,5)] else []
++ concatMap (\j -> if j < 1 then [(1,j)] else []) [6..20]
++ concatMap (\i -> concatMap (\j -> if j < i then [(i,j)] else []) [5..20]) [2..10]
-- Reduce `5 < 1` to `False`:
==> if False then [(1,5)] else []
++ concatMap (\j -> if j < 1 then [(1,j)] else []) [6..20]
++ concatMap (\i -> concatMap (\j -> if j < i then [(i,j)] else []) [5..20]) [2..10]
-- Reduce `if False x else y` to `y`
==> []
++ concatMap (\j -> if j < 1 then [(1,j)] else []) [6..20]
++ concatMap (\i -> concatMap (\j -> if j < i then [(i,j)] else []) [5..20]) [2..10]
-- Reduce the first `++` with its definition's first equation
==> concatMap (\j -> if j < 1 then [(1,j)] else []) [6..20]
++ concatMap (\i -> concatMap (\j -> if j < i then [(i,j)] else []) [5..20]) [2..10]
.
.
.
This process is different and less powerful than unification.