78

I have been reading many articles trying to understand the difference between functional and logic programming, but the only deduction I have been able to make so far is that logic programming defines programs through mathematical expressions. But such a thing is not associated with logic programming.

I would really appreciate some light being shed on the difference between functional and logic programming.

Wouter Beek
  • 3,307
  • 16
  • 29
Heshan Perera
  • 4,592
  • 8
  • 44
  • 57
  • 22
    Have you tried attempting to programme using both types of language? The difference will become obvious. – Marcin Nov 28 '11 at 14:51
  • 3
    One could say that a logic PL (i.e. Prolog) is a domain specific functional language suited for certain search & unification problems. – Ingo Nov 28 '11 at 14:53
  • 16
    One could say that a functional language (e.g., Haskell) is a domain specific logic programming language suited for certain problems that do not require a lot of search or declarative generality (i.e., functions are usable in only one direction, not as general relations which are usable in all directions). – mat Nov 28 '11 at 15:19
  • @Marcin Actually, it is possible to combine both of these paradigms in one language. It is definitely possible to [implement a logic programming system in Haskell](http://stackoverflow.com/questions/1932770/haskell-vs-prolog-comparison). – Anderson Green Jun 06 '16 at 21:23
  • 2
    @AndersonGreen It's possible implement any style of programming in any other turing-complete programming language. That's not especially interesting. – Marcin Jun 07 '16 at 00:01
  • 2
    @Marcin Fortunately, are several [functional logic programming](https://en.wikipedia.org/wiki/Functional_logic_programming) languages that combine these paradigms in a more convenient way. – Anderson Green Jun 07 '16 at 01:07
  • I disagree that it's obvious. I coded in both and while they look different but since they are both declarative. Articulating the essential difference is not straightforward. – Sridhar Sarnobat Sep 10 '21 at 23:36

7 Answers7

69

I wouldn't say that logic programming defines programs through mathematical expressions; that sounds more like functional programming. Logic programming uses logic expressions (well, eventually logic is math).

In my opinion, the major difference between functional and logic programming is the "building blocks": functional programming uses functions while logic programming uses predicates. A predicate is not a function; it does not have a return value. Depending on the value of it's arguments it may be true or false; if some values are undefined it will try to find the values that would make the predicate true.

Prolog in particular uses a special form of logic clauses named Horn clauses that belong to first order logic; Hilog uses clauses of higher order logic.

When you write a prolog predicate you are defining a horn clause: foo :- bar1, bar2, bar3. means that foo is true if bar1, bar2 and bar3 is true. note that I did not say if and only if; you can have multiple clauses for one predicate:

foo:-
   bar1.
foo:-
  bar2.

means that foo is true if bar1 is true or if bar2 is true

Some say that logic programming is a superset of functional programming since each function could be expressed as a predicate:

foo(x,y) -> x+y.

could be written as

foo(X, Y, ReturnValue):-
   ReturnValue is X+Y.

but I think that such statements are a bit misleading

Another difference between logic and functional is backtracking. In functional programming once you enter the body of the function you cannot fail and move to the next definition. For example you can write

abs(x) -> 
   if x>0 x else -x

or even use guards:

abs(x) x>0 -> x;
abs(x) x=<0 -> -x.

but you cannot write

abs(x) ->
   x>0,
   x;
abs(x) ->
   -x.

on the other hand, in Prolog you could write

abs(X, R):-
   X>0,
   R is X.
abs(X, R):-
   R is -X.

if then you call abs(-3, R), Prolog would try the first clause, and fail when the execution reaches the -3 > 0 point but you wont get an error; Prolog will try the second clause and return R = 3.

I do not think that it is impossible for a functional language to implement something similar (but I haven't used such a language).

All in all, although both paradigms are considered declarative, they are quite different; so different that comparing them feels like comparing functional and imperative styles. I would suggest to try a bit of logic programming; it should be a mind-boggling experience. However, you should try to understand the philosophy and not simply write programs; Prolog allows you to write in functional or even imperative style (with monstrous results).

vmg
  • 4,176
  • 2
  • 20
  • 33
Thanos Tintinidis
  • 5,828
  • 1
  • 20
  • 31
  • 10
    "I do not think that it is impossible for a functional language to implement something similar": Prolog interpreters written in Lisp or Lisp interpreters written in Prolog are easy to find. Both are Turing-complete languages. There is also the Functional-Logic Programming language Curry that combines both paradigmas. – twinterer Nov 28 '11 at 15:25
  • @twinterer what I meant is that there could be a functional language with backtracking (your second interpretation). Curry looks interesting! – Thanos Tintinidis Nov 28 '11 at 17:42
  • 5
    Mercury also has true functions (including higher-order functions) that return values, and supports backtracking. In this sense you could regard it as a functional language that has implemented backtracking. However it also has predicates and uses a prolog-like syntax, and logic programming is the normal mode of programming in it. I find the combination very useful. http://www.mercury.csse.unimelb.edu.au/ – Ben Nov 29 '11 at 01:22
  • 3
    s(X). Regarding higher-order capabilities: For a long time, Prolog support for [tag:meta-predicate]s left a lot to be desired compared to functional languages, both regarding features, performance and portability. Quite recently (in 2012, to be precise) ISO-Prolog got a boost in that direction with the completion of TC2 ("technical corrigendum 2"). Today, these features are widespread, pick up a Prolog manual and look up `call/2..8`. And... use it! – repeat Oct 30 '15 at 13:42
  • What problems are better solved using logic programming instead of functional or imperative propgramming? – skan May 21 '21 at 09:58
  • @Thanos Tintinidis , what do you mean by "a predicate is not a function"? To my knowledge, one can perfectly view a predicate as a function from the space of all input variables to the space of booleans (coming from Java, I'm thinking there of the interface Predicate as being mathematically equivalent to the interface Function, even though in Java they decided not to derive one from the other) – Sebastian Aug 11 '21 at 13:05
  • @Sebastian you can certainly _view_ it like that (although this only works for fully instantiated arguments, otherwise it's more like -> (bool, values for args)) and write programs, but imho it'd be akin to using a functional language to write otherwise procedural code. Ie I think that it's more idiomatic to think of a predicate as something that's true and imposes restrictions. – Thanos Tintinidis Aug 14 '21 at 16:53
  • @ThanosTintinidis thanks for your reply. I guess I agree with you that the whole point of logic programming is to treat predicates differently than functions in functional programming. It was just that your statement of a predicate not being a function had seemed to strong for me, but with your answer I'm fine. – Sebastian Aug 16 '21 at 09:03
31

In a nutshell:

In functional programming, your program is a set of function definitions. The return value for each function is evaluated as a mathematical expression, possibly making use of passed arguments and other defined functions. For example, you can define a factorial function, which returns a factorial of a given number:

factorial 0 = 1                       // a factorial of 0 is 1
factorial n = n * factorial (n - 1)   // a factorial of n is n times factorial of n - 1 

In logic programming, your program is a set of predicates. Predicates are usually defined as sets of clauses, where each clause can be defined using mathematical expressions, other defined predicates, and propositional calculus. For example, you can define a 'factorial' predicate, which holds whenever second argument is a factorial of first:

factorial(0, 1).               // it is true that a factorial of 0 is 1
factorial(X, Y) :-             // it is true that a factorial of X is Y, when all following are true:
    X1 is X - 1,                   // there is a X1, equal to X - 1,
    factorial(X1, Z),              // and it is true that factorial of X1 is Z, 
    Y is Z * X.                    // and Y is Z * X

Both styles allow using mathematical expressions in the programs.

socha23
  • 10,171
  • 2
  • 28
  • 25
  • I would like to see an explanation of the difference between `=` and `:-` here, I think that's a key to understanding the distinction. +1 for a good answer anyway. – luqui Nov 28 '11 at 15:13
  • @socha23: This example does not illustrate much of the difference between both paradigms since `factorial(X,3)` will give an instantiation error. So the only difference to a functional program would be that in Prolog you get that error dynamically. But ideally, you could also prove that there is no solution for above query. In fact constraints, like [library(clpfd)](http://www.swi-prolog.org/man/clpfd.html) are able to give you the right answer. See for `n_factorial/2`. There are two implementations: a naive, and a [more efficient one](http://www.swi-prolog.org/man/clpfd.html#zcompare/3). – false Nov 28 '11 at 16:01
  • Wow, I've been skimming over basic intros to prolog today and they way you've explained your prolog example really blew my mind. Pretty sure that's the way I should have been reading and thinking of prolog the whole time. – theonlygusti Sep 26 '17 at 21:20
  • The `:-` symbol means something like "the truth of the left-hand side is implied by the truth of the right-hand side". So it is not like assignment or equality, which are what `=` would mean in the imperative or functional paradigms respectively. – kaya3 Aug 09 '21 at 11:04
24

First, there are a lot of commonalities between functional and logic programming. That is, a lot of notions developed in one community can also be used in the other. Both paradigms started with rather crude implementations and strive towards purity.

But you want to know the differences.

So I will take Haskell on the one side and Prolog with constraints on the other. Practically all current Prolog systems offer constraints of some sort, like B, Ciao, ECLiPSe, GNU, IF, Scryer, SICStus, SWI, YAP, XSB. For the sake of the argument, I will use a very simple constraint dif/2 meaning inequality, which was present even in the very first Prolog implementation - so I will not use anything more advanced than that.

What functional programming is lacking

The most fundamental difference revolves around the notion of a variable. In functional programming a variable denotes a concrete value. This value must not be entirely defined, but only those parts that are defined can be used in computations. Consider in Haskell:

> let v = iterate (tail) [1..3] 
> v
[[1,2,3],[2,3],[3],[],*** Exception: Prelude.tail: empty list

After the 4th element, the value is undefined. Nevertheless, you can use the first 4 elements safely:

> take 4 v
[[1,2,3],[2,3],[3],[]]

Note that the syntax in functional programs is cleverly restricted to avoid that a variable is left undefined.

In logic programming, a variable does not need to refer to a concrete value. So, if we want a list of 3 elements, we might say:

?- length(Xs,3).
   Xs = [_A,_B,_C].

In this answer, the elements of the list are variables. All possible instances of these variables are valid solutions. Like Xs = [1,2,3]. Now, lets say that the first element should be different to the remaining elements:

?- length(Xs,3), Xs = [X|Ys], maplist(dif(X), Ys).
   Xs = [X,_A,_B], Ys = [_A,_B], dif(X,_B), dif(X,_A).

Later on, we might demand that the elements in Xs are all equal. Before I write it out, I will try it alone:

?- maplist(=(_),Xs).
   Xs = []
;  Xs = [_A]
;  Xs = [_A,_A]
;  Xs = [_A,_A,_A]
;  Xs = [_A,_A,_A,_A]
;  ... .

See that the answers contain always the same variable? Now, I can combine both queries:

?- length(Xs,3), Xs = [X|Ys], maplist(dif(X), Ys), maplist(=(_),Xs).
   false.

So what we have shown here is that there is no 3 element list where the first element is different to the other elements and all elements are equal.

This generality has permitted to develop several constraint languages which are offered as libraries to Prolog systems, the most prominent are CLPFD and CHR.

There is no straight forward way to get similar functionality in functional programming. You can emulate things, but the emulation isn't quite the same.

What logic programming is lacking

But there are many things that are lacking in logic programming that make functional programming so interesting. In particular:

Higher-order programming: Functional programming has here a very long tradition and has developed a rich set of idioms. For Prolog, the first proposals date back to the early 1980s, but it is still not very common. At least ISO Prolog has now the homologue to apply called call/2, call/3 ....

Lambdas: Again, it is possible to extend logic programming in that direction, the most prominent system is Lambda Prolog. More recently, lambdas have been developed also for ISO Prolog.

Type systems: There have been attempts, like Mercury, but it has not caught on that much. And there is no system with functionality comparable to type classes.

Purity: Haskell is entirely pure, a function Integer -> Integer is a function. No fine print lurking around. And still you can perform side effects. Comparable approaches are very slowly evolving.

There are many areas where functional and logic programming more or less overlap. For example backtracking and lazyness and list comprehensions, lazy evaluation and freeze/2, when/2, block. DCGs and monads. I will leave discussing these issues to others...

false
  • 10,264
  • 13
  • 101
  • 209
  • 2
    This is a very good answer for contrasting Haskell with Prolog. I'm not so sure it really gets to the heart of what the logic and functional paradigms are about, rather getting caught up in particular language details. There are other functional languages that aren't lazy for example, and there are logic programming languages that don't have unbound variable aliasing and so can't do your example. Mercury is the one I know about; it has just about everything you say logic programming as a whole is lacking, yet your length/maplist example wouldn't work in it. Still definitely logic programming. – Ben Nov 29 '11 at 01:36
  • Also, a minor correction: Mercury does in fact have type classes in its type system now. I believe they're equivalently powerful to Haskell's type classes (at least standard Haskell; probably not all of GHC's extensions). The big difference is Haskell's type classes are used usefully throughout the standard library, whereas Mercury's standard library design mostly pre-dates the introduction of type classes. – Ben Nov 29 '11 at 01:38
  • @Ben I agree with you and will try to avoid the misleading statements about Mercury. I have been watching the Mercury development since 1995 or so. For long time it seemed that the language would evolve more in the indicated direction. In particular with the HAL project. Now, in the light of your comments I view Mercury much more like a language in between. Actually, more on the functional side. But as you say, it is *still definitely logic programming*. – false Nov 29 '11 at 16:48
  • I guess you mean what Prolog is missing. Logic programming is bigger than Prolog. –  Apr 04 '14 at 22:03
20

Logic programming and functional programming use different "metaphors" for computation. This often affects how you think about producing a solution, and sometimes means that different algorithms come naturally to a functional programmer than a logic programmer.

Both are based on mathematical foundations that provide more benefits for "pure" code; code that doesn't operate with side effects. There are languages for both paradigms that enforce purity, as well as languages that allow unconstrained side effects, but culturally the programmers for such languages tend to still value purity.

I'm going to consider append, a fairly basic operation in both logical and functional programming, for appending a list on to the end of another list.

In functional programming, we might consider append to be something like this:

append [] ys = ys
append (x:xs) ys = x : append xs ys

While in logic programming, we might consider append to be something like this:

append([], Ys, Ys).
append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs).

These implement the same algorithm, and even work basically the same way, but they "mean" something very different.

The functional append defines the list that results from appending ys onto the end of xs. We think of append as a function from two lists to another list, and the runtime system is designed to calculate the result of the function when we invoke it on two lists.

The logical append defines a relationship between three lists, which is true if the third list is the elements of the first list followed by the elements of the second list. We think of append as a predicate that is either true or false for any 3 given lists, and the runtime system is designed to find values that will make this predicate true when we invoke it with some arguments bound to specific lists and some left unbound.

The thing that makes logical append different is you can use it to compute the list that results from appending one list onto another, but you can also use it to compute the list you'd need to append onto the end of another to get a third list (or whether no such list exists), or to compute the list to which you need to append another to get a third list, or to give you two possible lists that can be appended together to get a given third (and to explore all possible ways of doing this).

While equivalent in that you can do anything you can do in one in the other, they lead to different ways of thinking about your programming task. To implement something in functional programming, you think about how to produce your result from the results of other function calls (which you may also have to implement). To implement something in logic programming, you think about what relationships between your arguments (some of which are input and some of which are output, and not necessarily the same ones from call to call) will imply the desired relationship.

hammar
  • 138,522
  • 17
  • 304
  • 385
Ben
  • 68,572
  • 20
  • 126
  • 174
  • `append/3` can also be used to generate or test all lists consisting of a singly repeated sequence! That is `append(Xs,Xs,XsXs)`! – false Nov 29 '11 at 11:36
  • 1
    Very nicely explained. I've never thought of it in just this way, and you've provided a new insight. Thank you. – dogwood Jan 16 '16 at 04:45
3

I think the difference is this:

  • imperative programming=modelling actions
  • function programming=modelling reasoning
  • logic programming =modelling knowledge

choose what fits your mind best

Philippe Boissonneault
  • 3,949
  • 3
  • 26
  • 33
  • Best answer I've read so far. None of the others are just hovering around what people like me are wondering rather than answering it. I guess both describe transformations, but say so in a different tense. Prolog and lisp say so in an "if then" style voice, while FP says it in a "here's what I do." As I type this, I'm seeing that there really is no material difference. It's just syntactic/cosmetic differences. – Sridhar Sarnobat Sep 10 '21 at 23:37
  • Come to think of it, in a way it's as trivial a difference as American English and British English. They are trivially mappable. – Sridhar Sarnobat Sep 10 '21 at 23:42
3

Prolog, being a logical language, gives you free backtracking, it's pretty noticeable.

To elaborate, and I precise that I'm in no way expert in any of the paradigms, it looks to me like logical programming is way better when it comes to solving things. Because that's precisely what the language does (that appears clearly when backtracking is needed for example).

m09
  • 7,490
  • 3
  • 31
  • 58
  • 3
    @luqui it was meant as a comment at first and I do not think it's that stupid even though it's not that detailed :) – m09 Nov 28 '11 at 15:26
-1

functional programming: when 6PM, light on. logic programming: when dark, light on.

Pigeon The
  • 17
  • 1