Imperative programming spells out exactly what happens next; even if it's some high-level abstraction like "findAnswer()", that's still telling us what happens next, and we can jump to the definition of "findAnswer" to see what lower-level step comes next (and so on).
In functional programming, we're still specifying "what to do next", but we're allowed to give a set of things to do; the language accumulates these tasks, and is free to perform them in any order, or even concurrently; the result will be the same regardless (due to confluence). For example in an expression like 'f(g(x), h(y), [a(b), c(d), e])' we're telling the system exactly what to do next, although it's free to perform these function calls in any order it wants (many real implementations choose to define a particular evaluation order, to make e.g. reasoning about performance easier).
In both of these paradigms, the solution to our problem is left implicit: we indicate a solution by the lack of next-steps.
In logic/constraint/goal-driven programming the answer to "what happens next?" is undefined; we haven't told the system what to do next, so it's undetermined. Instead we've told the system when to stop: we make the solution explicit and the next-step implicit. The runtime system has to guess what to try, so it shuffles symbols around and around, stopping if it stumbles upon anything we've designated as a solution. Again, real implementations do define their evaluation order more explicitly for the sake of performance (e.g. depth-first search for Prolog).
Imperative programming spells out exactly what happens next; even if it's some high-level abstraction like "findAnswer()", that's still telling us what happens next, and we can jump to the definition of "findAnswer" to see what lower-level step comes next (and so on).
In functional programming, we're still specifying "what to do next", but we're allowed to give a set of things to do; the language accumulates these tasks, and is free to perform them in any order, or even concurrently; the result will be the same regardless (due to confluence). For example in an expression like 'f(g(x), h(y), [a(b), c(d), e])' we're telling the system exactly what to do next, although it's free to perform these function calls in any order it wants (many real implementations choose to define a particular evaluation order, to make e.g. reasoning about performance easier).
In both of these paradigms, the solution to our problem is left implicit: we indicate a solution by the lack of next-steps.
In logic/constraint/goal-driven programming the answer to "what happens next?" is undefined; we haven't told the system what to do next, so it's undetermined. Instead we've told the system when to stop: we make the solution explicit and the next-step implicit. The runtime system has to guess what to try, so it shuffles symbols around and around, stopping if it stumbles upon anything we've designated as a solution. Again, real implementations do define their evaluation order more explicitly for the sake of performance (e.g. depth-first search for Prolog).