Did you know ... | Search Documentation: |
Packs (add-ons) for SWI-Prolog |
Title: | Function application and composition |
---|---|
Rating: | |
Latest version: | 0.4.2 |
SHA1 sum: | 0e275370bccef5e4eb517314c63a1abe8cbc94be |
Author: | Michael Hendricks <michael@ndrix.org> |
Maintainer: | Michael Hendricks <michael@ndrix.org> |
Packager: | Michael Hendricks <michael@ndrix.org> |
Home page: | https://github.com/mndrix/func/ |
Download URL: | https://github.com/mndrix/func/archive/v0.4.2.zip |
Requires: | function_expansion |
list_util |
Version | SHA1 | #Downloads | URL |
---|---|---|---|
0.0.1 | af0c481261d75c4a624c18b68bbf7dc5d5cdeb5a | 2 | http://packs.ndrix.com/func/func-0.0.1.tgz |
0.0.2 | c7359120e6342af5485984eb4ae2e4cc2e7c68fb | 4 | http://packs.ndrix.com/func/func-0.0.2.tgz |
0.1.0 | 477316acc0210861e668a14dce78f17a10a482da | 1 | http://packs.ndrix.com/func/func-0.1.0.tgz |
0.1.1 | f6357d6cb920aadb5532ad9ea7e5420adb3ddef7 | 17 | http://packs.ndrix.com/func/func-0.1.1.tgz |
0.2.0 | f5489ff325c9feaa1e5b27cfce47aac4ea79e6f9 | 4 | http://packs.ndrix.com/func/func-0.2.0.tgz |
0.3.0 | 8a9184e240d91e8d49b691198b533205fe9095d7 | 6 | http://packs.ndrix.com/func/func-0.3.0.tgz |
0.4.0 | a833be0ecb448eb93989d8287e15c4376122e12b | 55 | http://packs.ndrix.com/func/func-0.4.0.tgz |
0.4.1 | b2ab054e9dbeb6181b34319841e84fc114aee25b | 43 | https://github.com/mndrix/func/archive/v0.4.1.zip |
0.4.2 | 0e275370bccef5e4eb517314c63a1abe8cbc94be | 261 | https://github.com/mndrix/func/archive/v0.4.2.zip |
:- use_module(library(func)). main :- % create a Plus3 function by composing three others Plus3 = succ of _+1 of plus(1), call(Plus3, 1, 4), format('~s world~n', [atom_codes $ hello]).
This module allows one to apply ($/2) and compose (of/2) terms as if they were functions. One often uses predicates as these functions, but one can define function behavior for arbitrary terms. See "What is a function" and "Defining functions" below.
Why? Prolog predicates are more powerful than functions, but sometimes
the syntax is awkward or requires meaningless effort from the
developer (generating and maintaining intermediate variable names and
goals). Using library(func)
often results in more succinct, clearer
code. For example, the use of atom_codes/2 in the Synopsis above.
At compile time, library(func)
converts function application and
composition into standard predicate calls. There should be no
performance penalty and one can still use nondeterminism.
For our purposes, a function is any term which can be converted into a
predicate call that accepts input in a single variable and produces
output by binding a single variable. The following sections describe
terms which library(func)
can natively treat as functions. See
further below for instructions on defining function behavior for
additional terms.
Any predicate whose final argument can be viewed as an "output" and whose penultimate argument can be viewed as "input" can be used, without modification, as a function.
For example, succ/2 can be seen as accepting an input as the first
argument and producing an output, in the second argument, that's one
greater. Similarly, the term plus(3)
can be seen as a predicate
which takes an integer input and generates an integer output that's
three larger.
Because Prolog predicates often follow a convention of having "inputs" before "outputs", many predicates can be applied and composed as functions as is. This includes length/2, reverse/2, maplist/3, append/3, etc.
An SWI-Prolog 7 dictionary is considered a function from its keys to its values. Applying the function to a non-existent key fails.
?- writeln(words{1:one, 2:two, 3:three} $ 2). two ?- writeln(words{1:one, 2:two, 3:three} $ 4). false.
This is similar to SWI Prolog's dot notation but doesn't throw an exception for missing keys. Dicts as functions can be composed and applied just like other functions.
Any arithmetic expression of a single variable can be applied and
composed as a function. For example, 2*_+3
is the function which
multiplies a number by two and then adds three. Similarly,
sqrt(X)*X + X/2
is a function even though it uses the input in
three different places.
A format string acceptable as the first argument to format/2 can be used as a function. It generates an atom, list of codes or string as output. The template's type determines the output's type. This offers a powerful string interpolation syntax visually similar to Python's.
In this next example, X might hold any of the values codes
,
chars
, number
or length
.
call('atom_~w' $ X, Atom, Term)
One might also use this interpolation syntax to build a file path:
Path = "/home/~w/src/~w/.git/config" $ [User, Project]
A compound term with a single ~
argument is considered a function which takes no input values and produces an output at the ~
position. For example,
atom(atom_string(~,"hello world")).
produces code that's equivalent to
atom_string(X,"hello world"), atom(X).
This can be conveniently employed with arithmetic expressions.
length(List, ~ is X + Y).
Because tilde terms take no inputs, they can't be used with $/2 or of/2.
Any term can behave as a function by defining additional clauses for the multifile hook compile_function/4. See the full documentation for greater detail. In this example, we'll define a list term as a function from a 0-based index to the corresponding element of that list.
:- multifile func:compile_function/4. func:compile_function(List, Index, Elem, nth0(Index, List, Elem)) :- is_list(List).
We might use it to convert small integers into English words:
N = 2, format('The number word is ~w~n', [zero,one,two,three] $ N).
One might imagine similar definitions for assoc lists, binary trees, hash tables, etc.
Using SWI-Prolog 6.3 or later:
$ swipl 1 ?- pack_install(func).
Source code available and pull requests accepted on GitHub: https://github.com/mndrix/func
Pack contains 12 files holding a total of 15.6K bytes.