FunctionalPerl
view release on metacpan or search on metacpan
docs/design.md view on Meta::CPAN
Check the [functional-perl website](http://functional-perl.org/) for
properly formatted versions of these documents.
---
# The design principles used in the functional-perl library
<with_toc>
## General
### Be properly functional first.
As already mentioned in the introduction on the [[howto]] page, the
modules are built using the functional paradigm from the ground up (as
much as makes sense; e.g. iterations in simple functions are often
written as loops instead of tail
recursion<small><sup>1</sup></small>). A sequences API to build
alternative implementations (like iterator based, or optimizing away
intermediate results) might be added in the future.
<small><sup>1</sup> But this is mainly done just because it's
(currently) faster, and since currently Perl does not offer
first-class continuations. Avoiding loop syntax and using function
calls everwhere makes it possible to suspend and resume execution
arbitrarily in a language like Scheme, without mutation getting in the
way; but this doesn't apply to current Perl 5.</small>
### Try to limit dependencies if sensible.
E.g. avoiding the use of `Sub::Call::Tail`, `Method::Signatures`,
`MooseX::MultiMethods` or `autobox` in the core modules. (Some tests,
[examples](../examples/README.md) and [Htmlgen](../htmlgen/README.md)
use them.) Declaring dependencies in `FunctionalPerl::Dependencies` so
that tests can skip modules with such dependencies.
### Generally provide functionality both as functions and methods.
*NOTE: since this was written, the method call based style has become
the primary way to provide functionality, and function based access
is spotty now. TODO: rewrite this section. BTW, providing functions
would be good but provide generic wrappers (which also work for
builtin types which can't have methods)--TODO.*
The sequence processing functions use the argument order conventions
from functional programming languages (Scheme, Ocaml, Haskell). The
methods move the sequence argument to the object position.
For example, both
list_map *inc, list (1,3,4)
and
list (1,3,4)->map (\&inc)
result in the same choice of algorithm. The shorter method name is
possible thanks to the dispatch on the type of the object. Compare to:
stream_map *inc, array_to_stream ([1,3,4])
or the corresponding
array_to_stream ([1,3,4])->map (\&inc)
which shows that there's no need to specify the kind of sequence
when using method syntax.
This actually needed an implementation trick: streams are just
lazily computed linked lists, hence the object on which the `map`
method is being called is just a generic promise. The promise could
return anything upon evaluation, not just a list pair. Thus it can't
be known what `map` implementation to call without evaluating the
promise. After evaluation, it's just a pair, though, at which point
it can't be known whether to call the `list_map` or `stream_map`
implementation. So how it works is that promises have a catch-all
(AUTOLOAD), which forces evaluation, and then looks for a method
with a `stream_` prefix first (which will find the `stream_map`
method in this example). If that fails, it will call the original
method name on the forced value.
So the way to make it work both for lazily and eagerly computed pairs
is to put both a `map` and a `stream_map` method into the
`FP::List::List` namespace (which is the parent class of
`FP::List::Pair` and `FP::List::Null`). When the pair was provided
lazily, the above implementation will dispatch to `stream_map`, which
normally makes sense since the user will want a lazy result from a
lazy input.
Note that this dispatch mechanism is only run for the first pair of
the list; afterwards, the code stays in either `list_map` or
`stream_map`(*). This means that prepending a value to a stream makes
the non-lazy map implementation be used:
cons (0, array_to_stream [1,3,4])->map (\&inc)
returns an eagerly evaluated list, not a stream. If that's not
what you want, you can still prefix the method name with `stream_`
yourself to force the lazy variant:
cons (0, array_to_stream [1,3,4])->stream_map (\&inc)
returns a stream.
(*) Question: should the dispatch really happen for each cell? Then
the eager part of a mixed list/stream would still be mapped eagerly,
and the lazy part lazily. (TODO: measure the overhead.)
NOTE: providing both functions and methods makes things more
complicated. The reason it was done so far is rather accidental, as
originally only functions were provided. Some functions like `car` and
`cons` are now wrappers that actually do method calls if they
can. `cons` still needs to remain a function because it doesn't
necessarily receive an object as its rest argument. TODO: figure out
whether to continue providing functions, perhaps reduce the offer to
those strictly needed and otherwise request the user to build them on
the fly using `the_method`? Or figure out a way to generate them for
whole packages easily. The second reason other than the need to use
`the_method` is that the functions can take arguments in the same
order as traditional functional programming languages (the object does
not need to come first, and with multiple objects it can be unclear
which to use as the one to dispatch on).
Idea: use `Class::Multimethods` or `Class::Multimethods::Pure` or
`MooseX::MultiMethods` to provide multimethods as alternative to
methods; this would allow to retain the traditional argument positions
and still use short names. (Perhaps look at Clojure as an example?)
### Use of `*foo` vs `\&foo`
Both of these work for passing a subroutine as a value, with the
following differences:
The code reference (`\&foo`):
- is the same type of data as what the expression `sub { .. }`
returns, and hence what's most often teached.
- clearly only ever represents a subroutine, whereas `*foo` is
ambiguous and can point to any type: the named package entries for
subroutines, IO handles, scalars, arrays, hashes, plus any other
kind of object by way of scalars.
- serialization to bytes is problematic (can only be done using
complex modules and only for a limited range of Perl code, and
includes serializing the whole code of the subroutine)
- can be used as a value in lexical variables as arguments to goto
even without using a `&` prefix, as in `my $f = \&foo; goto $f`
The glob (`*foo`):
- looks arguably visually cleaner, and may be easier to type
- later redefinitions to the subroutine it points to are being
reflected (as it points to the subroutine indirectly by name)
- can be serialized easily (as it's just a *name*)
- nicer for debugging when not using `show` from `FP::Show` or other
ways using introspection, as one can directly see the subroutine
package and name, not just an anonymous code ref,
- this code fails: `my $f = *foo; goto $f`. But this still works:
`my $f = *foo; goto &$f`. (`Sub::Call::Tail`'s `tail` is fine.)
- there are no builtin perl checks for the wrong type, i.e. passing
`*foo` where an array reference is expected will silently access
the `@foo` package variable, even if it was never declared (empty
in this case), while passing `\&foo` would have the interpreter
point out the error.
Quick benchmarking of subroutine calls of the two variants did not
detect a performance difference.
`FP::Predicate`'s `is_procedure` accepts globs if they contain a value
in the CODE slot, i.e. it adapts its meaning to "*can* represent a
subroutine". (But Todo: should it return true for any other callable
(overloaded object) as well? (How can the latter be implemented, by
way of checking for a '(&' method?))
Earlier versions of FunctionalPerl suggested to use globs. Due to
globs being a usually rarely used feature in Perl, and the possibility
for its mutation on a distance, it does not recommend it any longer,
and all code and examples have been changed to use `\&` instead.
( run in 1.765 second using v1.01-cache-2.11-cpan-e86d8f7595a )