Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Re: Programming Examples: Declarative, Imperative, Functional, Etc.

by tobyink (Abbot)
on Jul 25, 2012 at 14:05 UTC ( #983669=note: print w/ replies, xml ) Need Help??


in reply to Programming Examples: Declarative, Imperative, Functional, Etc.

Programming styles are like beard styles. People give names to different styles, but there's a lot of overlap and grey areas between them, and some styles simply defy categorisation.

One of the nice things about Perl is that it pretty much allows you to take your pick. You can choose a programming style that is appropriate to the task at hand.

Here's a brief rundown of the styles you mentioned...

declarative programming

A big umbrella term.

If you use SQL, then you already do this. In a declarative programming style you describe the results that you want, but not how to get there. When you're writing SQL, you don't describe what order to do cross-table joins in; you don't tell the engine which indexes to consult; you just tell it what data you want, and the SQL engine figures out the best way to get it.

There's a programming language called Prolog which is beloved of computer science departments worldwide but little used outside academia which is a declarative logic programming language. You tell it facts like lives(John, London) and based_at(GlaxoSmithKline, London) and ask it queries like potentialEmployer(John, ?) at it will do all the inferencing and searching and stuff.

Logical programming like Prolog is pretty much the only fully declarative programming style. Most other programming styles tend to mix declarative and imperative aspects. As I said, this is big umbrella term.

imperative programming

The other umbrella.

Anything that isn't declarative can be categorised as imperative. The word "imperative" comes from the Latin "impero" meaning "I command". It's the same place we get "emperor" from, and that's quite apt. You're the emperor. You give the computer little orders to do and it does them one at a time and reports back.

This is quite imperative:

my $x = 4; my $y = 3; my $z = $x * $y; say $z;

Now we'll get to the other programming styles which are somewhere between.

procedural programming

Procedural programming is imperative programming where you're allowed to write reusable subs/functions. Pretty much every imperative programming language allows you to write these now, so it's almost a synonym for imperative programming.

functional programming

In functional programming you compose your program of short functions. All code is within a function. All variables are scoped to the function. Whatsmore, within a function, you cannot modify any variables.

Sounds like quite a restriction, doesn't it? But actually you can get most stuff done pretty elegantly. And the restrictions add up to create a situation which is very good for running computations in parallel.

Haskell is a good example of a functional programming language. The first Perl 6 implementation (Pugs) was written in Haskell.

If you find yourself using, grep, map and the goodies from List::Util and List::MoreUtils a lot, then chances are you'd be very comfortable with functional programming.

Functional programming is generally considered to be quite a declarative style. If you're writing a grep or map in Perl, you probably don't really care about which order the iterations are evaluated in (though you often care what order the results are returned in), and you probably don't care whether they're evaluated in series or in parallel. That is, you're caring about the results not how they're calculated... declarative.

Other paradigms common in functional programming include currying and more generally first-class functions (i.e. functions which are a datatype in their own right and can be passed to other functions as parameters - this is very much possible in Perl).

object-oriented programming

In object oriented programming you create complex data structures known as objects to model the problem domain. Objects tend to have properties (e.g. an object representing a person might have a name and a date of birth as properties) and methods (i.e. things they can do).

Most object-oriented programming languages these days are also class-oriented. This means that all objects have to be members of a specific class, which defines what methods and properties it may have. Java is the classic example of a class-oriented programming language.

A notable example of a non class-oriented, but object-oriented programming language is Javascript which uses prototypes to provide sort of templates for the construction of new objects.

Big questions in object oriented programming are whether languages should support single or multiple inheritance; and whether objects should be mutable or not. (Immutable objects bring aspects of functional programming to OO.)

Object oriented programming is kinda declarative, but the methods are usually defined quite procedurally. Of course, the principle of encapsulation says that we must ignore how the methods are internally implemented. Pay no attention to that man behind the curtain! Therefore by implication, object oriented programming is completely declarative. :-)

Object-oriented programming is quite commonly done in Perl these days, particularly class-oriented programming because Perl makes that so easy. Other non-class-based OO can usually be pulled off in Perl too, but some are harder than others.

Moose gives you a really good framework for class-based and role-based OO in Perl.

Quite often people will categorise languages into these different styles, but it's really a mistake to do so. It is a oft-quoted witticism that you can write FORTRAN in any programming language. FORTRAN was (technically is) the quintessential imperative programming language. It did support function calls (i.e. procedural programming) but (I may be getting a little technical here) because these weren't implemented using a stack they were little more than syntactic sugar for GOTO.

So what people mean is that it's possible to write in a horrible, nasty imperative style (yes, imperative programming is generally looked down upon) even in languages not thought of as imperative. For example in Java you can create a single class with a single method and code your entire program into that method. And although C is generally considered procedural, via careful naming conventions and calling conventions for functions, and good use of structs, you can program in quite an object-oriented style.

Of course, sometimes your choice of language can make a particular style quite tricky and ugly. You have to jump through a lot of hoops to achieve anything even vaguely like first-class functions in Java, making many functional programming techniques difficult.

OK, some practical examples. These are several different ways to sum a list of numbers...

imperative
use 5.010; my @numbers = qw(1 3 5 6 7 8); my $sum = 0; for my $num (@numbers) { $sum += $num; } say $sum;
procedural
use 5.010; my @numbers = qw(1 3 5 6 7 8); sub sum { my $sum = 0; for my $num (@_) { $sum += $num; } return $sum; } say sum(@numbers);
functional
use 5.010; my @numbers = qw(1 3 5 6 7 8); sub reduce (&@) { my ($code, $first, @rest) = @_; return $first unless @rest; return $code->( $first, reduce($code, @rest), ); } sub sum { return reduce { $_[0] + $_[1] } @_; } say sum @numbers;

Which looks very complicated, but actually the reduce function is incredibly reusable for other tasks, and many programming languages will provide a reduce function out of the box. (Perl's List::Util module provides one and has been included in the Perl core since 5.8. I didn't use their one but wrote my own as an illustration.)

Interesting thing to notice: of the examples here, this is the only one that doesn't contain a looping construct. (The recursive nature of reduce is used instead.)

object-oriented
use 5.010; use IO::Handle; my @numbers = qw(1 3 5 6 7 8); { package Sum; use Any::Moose; has total => (is => 'rw', isa => 'Num', default => 0); sub add { my ($self, $num) = @_; $self->total( $self->total + $num ); } } my $answer = Sum->new; $answer->add($_) for @numbers; STDOUT->say( $answer->total );

Perl (more than any other language I've tried) gives you tremendous freedom to take your pick of programming style depending on the task at hand. Some new styles even seem to be developing within Perl that don't seem to exist elsewhere. (One I can think of appears to be the emerging habit of using Moose objects as memoization containers and lazy evaluators for complex procedures.)

perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'


Comment on Re: Programming Examples: Declarative, Imperative, Functional, Etc.
Select or Download Code
Re^2: Programming Examples: Declarative, Imperative, Functional, Etc.
by Anonymous Monk on Jul 25, 2012 at 15:33 UTC
    like beard styles... but there's a lot of ... grey areas
    Amen Brother.

    TJD

Re^2: Programming Examples: Declarative, Imperative, Functional, Etc.
by aaron_baugher (Deacon) on Jul 27, 2012 at 15:23 UTC

    Thanks very much for the detailed explanation and examples! I understand the differences much better now.

    I think part of my problem was that I assumed these were all different choices at the same level, when they aren't. Now I see declarative/imperative at one level, with procedural/functional/object-oriented at a higher level. In other words, your procedures/functions/objects may contain code that is imperative or declarative or (most likely) some mix of the two.

    I think the other problem was that I got hung up on the definition of declarative as (from the Wiki page), "describing what the program should accomplish, rather than describing how to go about accomplishing it." It seems to me that unless you're writing machine code at the lowest level, that's always the case to some extent. For instance, if I write:

    my $x = 3;

    I don't care how or where perl stores the value of $x, whether it stores it as a string or integer or floating point value, how many bytes it takes, or how it marks it as belonging to the current scope. I only care that when I write $x again, perl will replace it with 3, and that it will disappear at the appropriate time when the current scope ends. So although that seems like a straightforward imperative command, there's also a lot that's declarative about it.

    I can see the distinction between that and purely declarative commands, though, so maybe I'm just being too picky.

    I've been working through Higher Order Perl and Paul Graham's On Lisp, both of which get into functional programming, and it looks excellent. It's just plain harder than the other methods, though, at least for me. Maybe that's because my background was heavily imperative: BASIC 7.0, then 8502 and Z80 machine language and assembly, then shell, some C, and then to Perl. I suppose had I started with something like Lisp, it would come more naturally. I think it's worth struggling through the learning curve, though.

    Aaron B.
    Available for small or large Perl jobs; see my home node.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://983669]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (6)
As of 2014-10-22 01:18 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (112 votes), past polls