Which features of Perl make it a functional progra

2020-02-08 04:19发布

Inspired a little by: https://stackoverflow.com/questions/30977789/why-is-c-not-a-functional-programming-language

I found: Higher Order Perl

It made me wonder about the assertion that Perl is a functional programming language. Now, I appreciate that functional programming is a technique (much like object oriented).

However I've found a list of what makes a functional programming language:

  • First Class functions
  • Higher Order Functions
  • Lexical Closures
  • Pattern Matching
  • Single Assignment
  • Lazy Evaluation
  • Garbage Collection
  • Type Inference
  • Tail Call Optimization
  • List Comprehensions
  • Monadic effects

Now some of these I'm quite familiar with:

Garbage collection, for example, is Perl reference counting and releasing memory when no longer required.

Lexical closures are even part of the FAQ: What is a closure? - there's probably a better article here: http://www.perl.com/pub/2002/05/29/closure.html

But I start to get a bit fuzzy on some of these - List Comprehensions, for example - I think that's referring to map/grep (List::Util and reduce?)

I anyone able to help me fill in the blanks here? Which of the above can Perl do easily (and is there an easy example) and are there examples where it falls down?

2条回答
Fickle 薄情
2楼-- · 2020-02-08 04:54

Useful things that are relevant:

Perl monks rant about functional programming

Higher Order Perl

C2.com functional programming definitions

First Class functions

In computer science, a programming language is said to have first-class functions if it treats functions as first-class citizens. Specifically, this means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures.

So in Perl:

my $print_something = sub { print "Something\n" };

sub do_something {
    my ($function) = @_;
    $function->();
}

do_something($print_something);

Verdict: Natively supported

Higher Order Functions

In mathematics and computer science, a higher-order function (also functional form, functional or functor) is a function that does at least one of the following:

  • takes one or more functions as an input

  • outputs a function

With reference to this post on perlmonks:

In Perl terminology, we often refer to them as callbacks, factories, and functions that return code refs (usually closures).

Verdict: Natively supported

Lexical Closures

Within the perl FAQ we have questions regarding What is a closure?:

Closure is a computer science term with a precise but hard-to-explain meaning. Usually, closures are implemented in Perl as anonymous subroutines with lasting references to lexical variables outside their own scopes. These lexicals magically refer to the variables that were around when the subroutine was defined (deep binding).

Closures are most often used in programming languages where you can have the return value of a function be itself a function, as you can in Perl.

This is explained perhaps a little more clearly in the article: Achieving Closure

sub make_hello_printer {
    my $message = "Hello, world!";
    return sub { print $message; }
}

my $print_hello = make_hello_printer();
$print_hello->()

Verdict: Natively supported

Pattern Matching

In the context of pure functional languages and of this page, Pattern Matching is a dispatch mechanism: choosing which variant of a function is the correct one to call. Inspired by standard mathematical notations.

Dispatch tables are the closest approximation - essentially a hash of either anonymous subs or code refs.

use strict;
use warnings;

sub do_it {
    print join( ":", @_ );
}
my $dispatch = {
    'onething'      => sub { print @_; },
    'another_thing' => \&do_it,
};

$dispatch->{'onething'}->("fish");

Because it's just a hash, you can add code references and anonymous subroutines too. (Note - not entirely dissimilar to Object Oriented programming)

Verdict: Workaround

Single Assignment

Any assignment that changes an existing value (e.g. x := x + 1) is disallowed in purely functional languages.4 In functional programming, assignment is discouraged in favor of single assignment, also called initialization. Single assignment is an example of name binding and differs from assignment as described in this article in that it can only be done once, usually when the variable is created; no subsequent reassignment is allowed.

I'm not sure perl really does this. The closest approximation might be references/anonymous subs or perhaps constant.

Verdict: Not Supported

Lazy Evaluation

Waiting until the last possible moment to evaluate an expression, especially for the purpose of optimizing an algorithm that may not use the value of the expression.

Examples of lazy evaluation techniques in Perl 5?

And again, coming back to Higher Order Perl (I'm not affiliated with this book, honest - it just seems to be one of the key texts on the subject).

The core concept here seems to be - create a 'linked list' in perl (using object oriented techniques) but embed a code reference at your 'end marker' that evaluates if you ever get that far.

Verdict: Workaround

Garbage Collection

"GarbageCollection (GC), also known as automatic memory management, is the automatic recycling of heap memory."

Perl does this via reference counting, and releasing things when they are no longer referenced. Note that this can have implications for certain things that you're (probably!) more likely to encounter when functional programming.

Specifically - circular references which are covered in perldoc perlref

Verdict: Native support

Type Inference

TypeInference is the analysis of a program to infer the types of some or all expressions, usually at CompileTime

Perl does implicitly cast values back and forth as it needs to. Usually this works well enough that you don't need to mess with it. Occasionally you need to 'force' the process, by making an explicit numeric or string operation. Canonically, this is either by adding 0, or concatenating an empty string.

You can overload a scalar to do different things in by using dualvars

Verdict: Native support

Tail Call Optimization

Tail-call optimization (or tail-call merging or tail-call elimination) is a generalization of TailRecursion: If the last thing a routine does before it returns is call another routine, rather than doing a jump-and-add-stack-frame immediately followed by a pop-stack-frame-and-return-to-caller, it should be safe to simply jump to the start of the second routine, letting it re-use the first routine's stack frame (environment).

Why is Perl so afraid of "deep recursion"?

It'll work, but it'll warn if your recursion depth is >100. You can disable this by adding:

no warnings 'recursion';

But obviously - you need to be slightly cautious about recursion depth and memory footprint.

As far as I can tell, there isn't any particular optimisation and if you want to do something like this in an efficient fashion, you may need to (effectively) unroll your recursives and iterate instead.

Tailcalls are supported by perl. Either see the goto ⊂ notation, or see the neater syntax for it provided by Sub::Call::Tail

Verdict: Native

List Comprehensions

List comprehensions are a feature of many modern FunctionalProgrammingLanguages. Subject to certain rules, they provide a succinct notation for GeneratingElements? in a list. A list comprehension is SyntacticSugar for a combination of applications of the functions concat, map and filter

Perl has map, grep, reduce.

It also copes with expansion of ranges and repetitions:

my @letters = ( "a" .. "z" ); 

So you can:

my %letters = map { $_ => 1 } ( "A" .. "z" ); 

Verdict: Native (List::Utils is a core module)

Monadic effects

... nope, still having trouble with these. It's either much simpler or much more complex than I can grok.

If anyone's got anything more, please chip in or edit this post or ... something. I'm still a sketchy on some of the concepts involved, so this post is more a starting point.

查看更多
【Aperson】
3楼-- · 2020-02-08 05:05

Really nice topic, I wanted to write an article titled something link "the camel is functional". Let me contribute with some code.

Perl also support this anonymous functions like

 sub check_config {
    my ( $class, $obj ) = @_;

    my $separator = ' > ';

    # Build message from class namespace.
    my $message = join $separator, ( split '::', $class );

    # Use provided object $obj or
    # create an instance of class with defaults, provided by configuration.
    my $object = $obj || $class->new;

    # Return a Function.
    return sub {
        my $attribute = shift;

        # Compare attribute with configuration,
        # just to ensure it is read from there.
        is $object->config->{$attribute},

            # Call attribute accessor so it is read from config,
            # and validated by type checking.
            $object->$attribute,

            # Build message with attribute.
            join $separator, ( $message, $attribute );
        }
}

sub check_config_attributes {
    my ( $class, $obj ) = @_;

    return sub {
        my $attributes = shift;
        check_config( $class, $obj )->($_) for (@$attributes);
        }
}
查看更多
登录 后发表回答