in reply to Re^2: Easy Text Adventures in Perl
in thread Easy Text Adventures in Perl

Still, I think more Perl children should learn logic programming and so I toss out candy to lure 'em in.
I've heard this statement and other similar statements any number of other times, but I'm afraid I've yet to fully grasp the reasoning behind them, so I was wondering if I might impose upon your good nature and inquire as to exactly why you feel people should learn Logic Programming in general (and prolog in specific?)? Is it just a general notion that the more paradigms we're exposed to, the more options you have when devising solutions? Is there perchance a specific example of a "real world" (for some value of real, obviously) problem that is vastly easier to solve in prolog and you could present here, hopefully with contrasting examples from multiple languages?

Replies are listed 'Best First'.
Re^4: Easy Text Adventures in Perl
by halley (Prior) on Feb 14, 2005 at 14:38 UTC
    It's like Eastern Philosophy versus Western Philosophy. Both are correct, both are wrong, and yet, you understand the world in a completely different way when you are comfortable with the tenets of more than one.

    In imperative programming, you instruct the computer what to DO. You have created a player-piano roll, and the player-piano will render the song.

    In logical programming, you instruct the computer about the known facts, and about the rules which govern the implications of unknown facts. You ask such a system a question, and the computer will scan through all of the facts and rules, recursing as necessary, until it has an answer (or it finds it impossible to solve).

    If you've written a Makefile, you have used a little bit of logical programming. You specify the rules for what files depend on others, and how to build them. The facts are not specified in the Makefile but are specified by the timestamps on the files themselves. The make program simply uses the facts and the rules to prove it can get to a point where the specified target is the newest file.

    However, saying that make is like Prolog is akin to saying that $a || $b is "lazy evaluation" (as was discussed here last week). It's a very very limited example, compared to the actual benefits of the methods.

    This site is religiously themed. If you're a Catholic, I urge you to visit a synagogue, a mosque and a buddhist temple. Visit each one until you see what is of value, and you see what is merely the trappings of an old tradition. Go back to your own church, and you'll more easily spot the catechisms which are Catholic, except by rote. The same goes for anyone of any faith or church, by the way.

    --
    [ e d @ h a l l e y . c c ]

Re^4: (why to use logic programming)
by Ovid (Cardinal) on Feb 14, 2005 at 17:02 UTC

    BUU asked:

    ... I was wondering if I might impose upon your good nature and inquire as to exactly why you feel people should learn Logic Programming in general (and prolog in specific?)? Is it just a general notion that the more paradigms we're exposed to, the more options you have when devising solutions?

    First: the reason I'm using Prolog for this is because I don't know Mercury, Gödel, or any other language that supports logic programming. However, since Prolog is far and away the most popular, I wasn't too worried about this. In other words, I think logic programming is important; the fact that I'm using Prolog is almost accidental. It also doesn't hurt that Prolog is very easy to parse.

    Now, on to the benefits!

    Do you ever use regular expressions? They're a simple form of logic programming. Of course, there's nothing in regexes that I really can't do with substr and a lot of imperative programming, but that would be silly if I have regular expressions at my disposal.

    Larry Wall also has some interesting things to say about logic programming (emphasis mine):

    Transactions are not just something that database programmers have to worry about. I think training in transactional thinking tends to fall through the paradigmatic cracks because transactions can't be classified as functions or events or objects. If anything, a transaction is most closely related to the logic programming paradigm, which is undertaught. A transaction can be viewed as a sort of hypothesis that can either be proven or disproven. A partially proven hypothesis is of little use.

    Logic programming is also used quite extensively in expert systems, natural language processing and artificial intelligence because it makes so many of the ideas contained in them very easy to express.

    Further, and this is a big plus, well-written prolog Programs can be as close to "programming from the spec" as you can get. For example, here's a little snippet derived from the famous "Clocksin/Mellish" programming text. Can you guess what we're defining?

    average_taxpayer(Person) :- not(foreigner(Person)), not( spouse(Person, Spouse), gross_income(Spouse, Income1), Income > 3000 ), gross_income(Person, Income2), ...

    That should not be terribly hard to figure out. If you have a program that needs to determine if someone is an "average" taxpayer, that's it. You would have a database with facts containing people's gross incomes or who is married to whom, but Prolog is often that straightforward.

    The key in the snippet above is that the various bits express relationships between things. If your program is mostly concerned with how a bunch of things are related to one another, logic programming is an excellent choice, but like regular expressions or closures, the utility is often not very apparent until one actually has a chance to use it. In that respect, it's like a foreign country: you can read about it all you want, but until you live there, it's tough to appreciate its culture.

    Update: In the type of programming that most Perl programmers do, we have our data and we keep munging that data until it eventually looks like our goal. In logic programming, we tell the computer what our goal looks like and we let the computer figure out how to get there. Is that straightforward enough? :)

    Cheers,
    Ovid

    New address of my CGI Course.

      The comparison to RegEx programming turned on a light bulb. Does that mean that (continuing the analogy) Prolog internals are as scary and byzantine (compared to its straightforward representation in code) as RegEx internals are to the straightforward (after you get the hang of them) representation of m// and its relatives?

      • comparing logic programming to RegEx programming; (nice analogy and starting point for discussion) (++)
      • straightforward description in the update (++)
      • giving Larry Wall credit for TimToadys posts (??)

        The internals of AI::Prolog might scare a few people, but generally, no, Prolog internals don't have to be as scary. The main issue (I think) with Perl's regex engine is that it is so powerful that people always thought it should do just a little bit more. So they made it do it. With hacks. Lots of 'em. For example, being able to embed code in regular expressions is natural because people really wanted to turn regular expressions into its own programming language. When you consider that we have perfectly good logic programming languages already out there ...

        Of course, if you mean how Prolog works "under the hood" without worrying too much about the actual implementation (i.e., what's the sequence of steps it takes to arrive at an answer), then I would say Prolog's internals are ridiculously straightforward.

        And gosh, why would I give Larry credit for posts as brilliant as TimToady's? ;)

        Cheers,
        Ovid

        New address of my CGI Course.

      What you say makes sense, as usual =], but I'm afraid I'm still having trouble thinking of specific examples where it would be better. You mention several fields such as AI and so forth, but the example you gave doesn't seem like that much of a win over an imperative language, unless I'm drastically missunderstanding it. Here is my interpretation of your prolog:
      sub is_average { my $self = shift; if( not $self->foreigner and not $self->spouse and $self->income > 3 +000 ) { return 1; } }
      Although I'm not sure where Income2 really enters in to it, perhaps my $self->income should return a list of incomes, but that's a minor detail, yes?

        Your example shows why logic programming beats imperative programming hands down (in this particular case). Let's flesh out my example. For the sake of simplicity, we'll just say average taxpayers are not foreigners, their spouses (if any) make less than 30000 and their income is less than 50000.

        foreigner(abdul). foreigner(marcus). spouse(edward, sally). spouse(bill, hillary). gross_income(hillary, 100000). gross_income(edward, 20000). gross_income(bill, 30000). gross_income(marcus, 49999). gross_income(mary, 35000). average_taxpayer(Person) :- not(foreigner(Person)), not(( spouse(Person, Spouse), gross_income(Spouse, Income1), Income1 > 30000 )), gross_income(Person, Income2), Income2 < 50000.

        So now it's trivial to find out if "marcus" is an average taxpayer (no) or if mary is (yes). You'd have to write a bit more Perl code than I would have to write Prolog code. However, this is the kicker. What if I want a list of all average taxpayers? Well, I know you have to have a gross income to be an average tax payer, so I issue the following query:

        gross_income(Person,_), average_taxpayer(Person).

        That will tell me that edward and mary are average taxpayers. I didn't have to write any extra code to do that. Logic programs can infer the answer. Perl would have a hard time keeping up with that. In fact, one of the fascinating things about logic programs is that they can all be reused in a similar manner. With imperative programming, reuse means "don't duplicate code." With logic programming, reuse means that, but it also means "use the same code to answer related but different questions."

        One thing to keep in mind about logic programming is that you're not looking at function calls. You're looking at relationship definitions and you don't have to write any extra code to express them.

        Cheers,
        Ovid

        New address of my CGI Course.

        #define IS_FOREIGNER(x) (!(x->citizen)) #define IS_SPOUSE(x) (x->married) #define INCOME(x) (x->income) ... int is_average( struct Person *person ) { if ( !IS_FOREIGNER( person ) && !IS_SPOUSE( person ) && INCOME( pe +rson ) > 3000 ) { return 1; } else { return 0; } }

        You wrote your code in an OO style. I wrote mine in an imperative style. Why is yours any better than mine? What benefits do I get by writing in an OO style over an imperative style? When would using OO be better than using imperative?

        While you're answering those questions, think about the fact that you're having to explain a completely new way of thinking about problem-solving to me. Think about the fact that you're having to explain concepts that may take months for me to fully comprehend. (And, that's assuming you have grokked those concepts, which you may not have.) Then, you have to answer the question "Why on earth would I ever want to do things that way?!?" And, you'll probably go "Uhh ... uhhh ..." and fumble for an answer.

        Now, think about the fact that Ovid is trying to do the same thing with you, but for a logic-based style of programming.

        For further reading, look over Beating the Averages, specifically the section entitled The Blub Paradox.

        Being right, does not endow the right to be rude; politeness costs nothing.
        Being unknowing, is not the same as being stupid.
        Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
        Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.