futils
Function Utilities Library
1 Introduction
futils
is a library that adds some abstractions for managing and transforming functions in Clojure.
Currently provided macros and functions are:
futils.args/
futils.named/
futils.utils/
frepeat
– returns a sequence generated by a function that uses named arguments.
2 Installation
Add dependencies to project.clj
:
[io.randomseed/futils
"``"]
Then (depending on which functions should be used) require it in your program:
(require 'futils.utils)
(require 'futils.args)
(require 'futils.named)
or:
(ns your-namespace
(:require [futils.utils :as utils])
(:require [futils.args :as args])
(:require [futils.named :as named]))
3 Usage
3.1 futils.args
The futils.args
namespace contains functions that provide positional arguments management, like counting arguments or transforming functions so they can accept variable number of arguments (with optional padding).
3.1.1 argc
Determines the number of arguments that the given function takes and returns a map containing the following keys:
:arities
– a sorted sequence containing number of arguments for all arities,:engine
::clj
– if metadata were used to determine arities – DEPRECATED);:jvm
– if Java reflection methods were used to determine arities),
:macro
– a flag informing whether the given object is a macro,:variadic
– a flag informing whether the widest arity is variadic.
Variadic parameter is counted as one of the possible arguments (if present).
The macro flag (:macro
key with value true
) is only present when macro was detected.
If the given argument cannot be used to obtain a Var bound to a functon or a function object then it returns nil
.
e.3.1 - Using argc
on anonymous functions
e.3.2 - Using argc
on named functions
e.3.3 - Using argc
on macros
3.1.2 relax
Returns a variadic function object that calls the given function f
, adjusting the number of passed arguments to the nearest matching arity. It cuts argument list or pads it with nil
values if necessary.
The arities will be obtained using JVM reflection calls to anonymous class representing a function object.
To determine the number of arguments the nearest arity is picked up by matching a number of passed arguments to number of arguments for each arity. If there is no exact match then the next arity capable of taking all arguments is selected.
If the expected number of arguments is lower than the number of arguments actually passed to a wrapper call then the exceeding ones will be ignored.
If the detected number of arguments that the original function expects is higher than the number of arguments really passed then nil
values (or other padding values) will be passed as extra arguments.
When a variadic function is detected and its variadic arity is the closest to a number of arguments passed then all of them will be used during a function call (no argument will be ignored).
The relax
takes optional named arguments:
:pad-fn
– a function that generates values for padding,:pad-val
– a value to use for padding instead ofnil
,:verbose
– a switch (defaults tofalse
) that if set to true causes wrapper to return a map containing additional information.
See relax*
for detailed descriptions of :pad-fn
and :verbose
options.
e.3.6 - Using relax
e.3.7 - Custom padding
e.3.8 - Verbose mode
3.1.3 relax*
Returns a variadic function object that calls the given function, adjusting the number of passed arguments to nearest matching arity. It cuts argument list or pads it with nil values if necessary.
It takes 1 positional, obligatory argument, which should be a function (f
) and two named, keyword arguments:
:arities
– a sorted sequence of argument counts for all arities,:variadic
– a flag informing whether the widest arity is variadic.
It also makes use of optional named arguments:
:pad-fn
– a function that generates values for padding,:pad-val
– a value to use for padding instead ofnil
,:verbose
– a switch (defaults tofalse
) that if set totrue
, causes wrapper to return a map containing additional information.
To determine the number of arguments the nearest arity is picked up by matching a number of passed arguments to each number from a sequence (passed as :arities
keyword argument). If there is no exact match then the next arity capable of handling all arguments is chosen.
If the expected number of arguments is lower than a number of arguments actually passed to a wrapper call, the exceeding ones will be ignored.
If the declared number of arguments that the original function expects is higher than the number of arguments really passed then nil
values (or other padding values) will be placed as extra arguments.
When a variadic function is detected and its variadic arity is the closest to the number of passed arguments then all of them will be used.
3.1.3.1 Verbose mode
If the :verbose
flag is set then the result will be a map containing the following:
:argc-received
– a number of arguments received by the wrapper,:argc-sent
– a number of arguments passed to a function,:argc-cutted
– a number of arguments ignored,:argc-padded
– a number of arguments padded withnil
values,:args-received
– arguments received by the wrapper,:args-sent
– arguments passed to a function,:arities
– a sorted sequence of argument counts for all arities,:arity-matched
– an arity (as a number of arguments) that matched,:engine
– a method used to check arities (:clj
or:jvm
),:result
– a result of calling the original function,:variadic
– a flag telling that the widest arity is variadic,:variadic-used
– a flag telling that a variadic arity was used,:verbose
– a verbosity flag (alwaystrue
in this case).
3.1.3.2 Padding function
If a padding function is given (with :pad-fn
) it should take keyword arguments. During each call the following keys will be set:
:argc-received
– a number of arguments received by the wrapper,:arity-matched
– an arity (as a number of arguments) that matched,:iteration
– a number of current iteration (starting from 1),:iterations
– a total number of iterations,:previous
– a value of previously calculated argument (the result of a previous call or a value of the last positional argument when padding function is called for the first time).
Values associated with :iteration
and :previous
keys will change during each call, the rest will remain constant.
If there is no last argument processed at a time when f
is called for the first time (because no arguments were passed), the :previous key is not added to a passed map. That allows to use a default value in a binding map of f
or to make easy checks if there would be some previous value (nil
is ambiguous).
e.3.10 - Using relax*
e.3.11 - Handling variadic arguments by relax*
e.3.12 - Using relax*
on anonymous functions
e.3.13 - Custom padding value
e.3.14 - Custom padding function
e.3.15 - Verbose mode
e.3.16 - Chaining with argc
e.3.17 - With great power comes great responsibility
e.3.18 - Handling invalid values
3.2 futils.named
The futils.named
namespace contains functions transforming functions taking positional arguments into functions that can handle named arguments.
3.2.1 apply
It works like apply
but handles named arguments. Takes function f
, an optional list of arguments (args*
) to be passed during a call to it and a map (args-map
) that will be decomposed and passed as named arguments.
Returns the result of calling f.
e.3.19 - Usage of apply
e.3.20 - Handling invalid values by apply
3.2.2 comp
where function
is a function object or a map containing :f
key associated with a function object and optional options controlling output transformations.
The comp
function takes a set of functions that accept named arguments and returns a function object that is the composition of those functions. The returned function takes named arguments, applies the rightmost of functions to the arguments, the next function (right-to-left) to the result, etc.
Each function should return a map or a sequential collection (see :use-seq
switch) that will be used to generate named arguments for the next function in the execution chain. If a function does not return a map its resulting value will be assigned to a key of newly created map. The default name of this key will be :out
unless the option :map-output
had been used (see the explanations below).
The returned value of the last called function is not transformed in any way and there is no need for it to be a map.
Functions can be expressed as function objects or as maps. In the second case the map must contain :f
key with function object assigned to it and may contain optional, controlling options which are:
:merge-args
:false
(default) ortrue
or a key,:map-output
:false
(default) ortrue
or a key,:rename-keys
:nil
(default) or a map,:post-rename
:nil
(default) or a map,:use-seq
:nil
(default) ortrue
,:apply-raw
:nil
(default) ortrue
.
The :merge-args
option, when is not set to false
nor nil
, causes function arguments to be merged with a returned map. If the key name is given they all will be stored under specified key of this map. If the assigned value is set to true
then they will be merged. If two keys are the same the association from arguments is overwritten by the entry being returned by a function.
The :map-output
causes the returned value to be stored under a specified key of a resulting map. If the option value is set to true
then the key name will be :out
.
The :rename-keys
option causes keys of a resulting map to be renamed according to the given map. The transformation will be performed on a returned value (if it's a map), before any other changes (output mapping or arguments merging).
The :post-rename
option works in the same way as :rename-keys
but it's performed after all other transformations are applied.
The :use-seq
option has the effect only if the returned value is a sequential collection having even number of arguments. If it's set then the sequence is changed into a map for further processing (including renaming keys) instead of being put as a value associated with some key.
The :apply-raw
option is for performance reasons. Use it with care. It disables checks and most of the transformations and causes wrapper to assume that the resulting structure is either single value, sequential collection or a map. If it's an atomic value or a map, it will change it into sequence ready to be applied as named arguments when calling the next function. If it's a sequence then nothing will be changed.
Defaults for the options described above may be given by passing a map as a first or last argument when calling the comp
function. Such a map should not contain :f
key.
The function returns a function object.
e.3.21 - Basic usage of comp
e.3.22 - Merging arguments with comp
e.3.23 - Mapping output with comp
e.3.24 - Renaming results with comp
e.3.25 - Using sequential results with comp
e.3.26 - Using raw results with comp
3.2.3 nameize
where arity-mappings…
are pairs expressed as: [names] {defaults}
or just [names]
.
The macro creates a wrapper that passes named arguments as positional arguments. It takes a funtion object (f
), a vector S-expression containing names of expected arguments (names
– expressed as keywords, symbols, strings or other objects) and an optional map S-expression with default values for named arguments (defaults
).
Since version 0.7.0 it accepts multiple arity mappings expressed as pairs consisting of argument name vectors and maps of default values (for all or some of the names).
The order of names in a vector is important. Each name will become a key of named argument which value will be passed to the given function on the same position as in the vector.
If unquoted symbol is given in a vector or in a map, it will be transformed into a keyword of the same name. Use quoted symbols if you want to use symbols as keys of named arguments.
If the &rest
special symbol is placed in a vector then the passed value that corresponds to its position will be a map containing all named arguments that weren't handled. If there are none, nil
value is passed.
The macro is capable of handling multiple arities. In such case the declared arities (e.g. [:a :b]
[:a :b :c]
) will be matched against the given named arguments (e.g. {:a 1 :b 2}
) by comparing declared argument names to key names. Firstly it will try to match them without considering default values (if any) and in case of no success (when there is no declared arity that can be satisfied by the given arguments) matching is preformed again but with default arguments merged. From the resulting collection of matching arity mappings the one element with the least requirements is chosen (that has the lowest count of declared arguments).
The result is a function object.
e.3.27 - Usage of nameize
e.3.28 - Handling multiple arities by nameize
e.3.29 - Handling invalid values by nameize
3.2.4 nameize*
where arity-mappings
is a pair expressed as: names defaults
or just names
.
The function creates a wrapper that passes named arguments as positional arguments. It takes a funtion object (f
), a vector S-expression containing names of expected arguments (names
– expressed as keywords, symbols, strings or other objects) and an optional map S-expression with default values for named arguments (defaults
).
Since version 0.7.0 it accepts multiple arity mappings expressed as pairs consisting of argument name vectors and maps of default values (for all or some of the names).
The order of names in a vector is important. Each name will become a key of named argument which value will be passed to the given function on the same position as in the vector.
If unquoted symbol is given in a vector or in a map, it will be transformed into a keyword of the same name. Use quoted symbols if you want to use symbols as keys of named arguments.
If the &rest
special symbol is placed in a vector then the passed value that corresponds to its position will be a map containing all named arguments that weren't handled. If there are none, nil
value is passed.
The function is capable of handling multiple arities. In such case the declared arities (e.g. [:a :b]
[:a :b :c]
) will be matched against the given named arguments (e.g. {:a 1 :b 2}
) by comparing declared argument names to key names. Firstly it will try to match them without considering default values (if any) and in case of no success (when there is no declared arity that can be satisfied by the given arguments) matching is preformed again but with default arguments merged. From the resulting collection of matching arity mappings the one element with the least requirements is chosen (that has the lowest count of declared arguments).
A function object is returned.
e.3.30 - Usage of nameize*
e.3.31 - Handling multiple arities by nameize*
3.3 futils.utils
The futils.utils
namespace contains functions providing various additional operations on functions and some helpers used by other parts of library.
3.3.1 frepeat
Returns a lazy sequence of results produced by the given function f
which should accept named arguments.
If the first argument passed to frepeat is a number (n
) and the second is a function (f
) it will limit the iterations to the specified count.
If the numeric argument is missing and only a function object is given the frepeat will produce infinite sequence of calls.
The last, optional argument should be a map (kvs
) that initializes named arguments that will be passed to the first and subsequent calls to f
.
Additionally each call to f
will pass the following keyword arguments:
:iteration
– a number of current iteration (starting from 1),:previous
– a result of the previous call tof
.
The first call to f
will pass the following:
:iteration
– 1,:iterations
– a total number of iterations (ifn
was given).
It is possible to set the initial value of :previous
if there is a need for that (by passing it to frepeat
) or shadow the value assigned to :iterations
after the first call (by setting it in the passed function f
).
Values associated with :iteration
and :previous
keys will always change during each call.