1 Introduction
1.1 Overview
std.lib.apply provides applicative invocation, allowing functions to be applied within a configurable runtime context.
Common uses include:
- Invoking a form with a default runtime
- Applying arguments within an explicit context
- Building host applicatives that wrap plain functions
2 Walkthrough
2.1 Host applicatives
A host applicative wraps a plain Clojure function or form so it can be invoked through the applicative interface. apply-in runs it in an explicit context, apply-as lets it pick its own context, and invoke-as accepts variadic arguments.
create and apply a host applicative
^{:refer std.lib.apply/host-applicative :added "3.0"}
(def adder (host-applicative {:form '+}))
^{:refer std.lib.apply/apply-in :added "3.0"}
(apply-in adder nil [1 2 3 4 5])
=> 15
^{:refer std.lib.apply/apply-as :added "3.0"}
(apply-as adder [10 20 30])
=> 60
^{:refer std.lib.apply/invoke-as :added "3.0"}
(invoke-as adder 1 2 3)
=> 6
2.2 Async execution
Adding :async true causes the applicative to return a future. Dereference it to get the result.
run an applicative asynchronously
^{:refer std.lib.apply/host-applicative :added "3.0"}
@((host-applicative {:form '+ :async true}) 1 2 3 4 5)
=> 15
3 Apply within a context
apply-in runs an applicative within a given runtime. apply-as picks the runtime automatically.
v 3.0
(defn apply-as
([app args]
(let [rt (or (:runtime app)
(protocol.apply/-apply-default app))
rt (if (fn? rt) (rt) rt)]
(apply-in app rt args))))
link
(apply-as (host-applicative {:form '+}) [1 2 3 4 5]) => 15
4 Host applicatives
host-applicative constructs a plain-function applicative that does not need an external context.
5 std.lib.apply: A Comprehensive Summary
The std.lib.apply namespace provides a mechanism for defining and interacting with "applicatives" – a concept often found in functional programming for applying functions within a context. This module is designed to abstract the application of functions, allowing for flexible execution environments and transformations of input and output.
5.1 Core Concepts:
- Applicative: An applicative is an object that encapsulates a function or a form to be evaluated, along with optional metadata about its execution (e.g.,
async). It implements thestd.protocol.apply/IApplicableprotocol.nIApplicableProtocol: This protocol defines the interface for applicatives, including:n-apply-default: Determines the default runtime for the applicative if not explicitly provided.n-transform-in: Transforms the input arguments before applying the function.n-transform-out: Transforms the result after the function has been applied.n* Context/Runtime: Applicatives can operate within a specified runtime (rt), which provides the execution context.
5.2 Key Functions and Macros:
apply-in:n Purpose: Runs an applicative within a given runtime context. It first transforms the input arguments using-transform-in, then applies the function using-apply-in, and finally transforms the output using-transform-out.n Usage:(apply-in app rt args)napply-as:n Purpose: Allows an applicative to automatically resolve its runtime context (either from its own:runtimefield or by calling-apply-default).n Usage:(apply-as app args)ninvoke-as:n Purpose: A convenience function that invokes an applicative with a variable number of arguments, internally callingapply-as.n Usage:(invoke-as app & args)nhost-applicative:n Purpose: Constructs a basicHostApplicativerecord that can execute a Clojure function or form directly in the current host environment. It can optionally execute asynchronously usingstd.lib.future.n Usage:(host-applicative {:form '+ :async true})nHostApplicative(defimpl record):n Purpose: The concrete implementation ofIApplicablefor the host environment. It takesfunction,form, andasyncas fields.n Protocol Implementation: Its-apply-default,-transform-in, and-transform-outmethods are simple pass-throughs ornilfor the host context, as no special transformations or default runtimes are needed.nhost-apply-in:n Purpose: A helper function used byHostApplicativeto perform the actual function application, handling both synchronous and asynchronous execution.
5.3 Usage Pattern:
The typical usage involves creating an applicative (e.g., host-applicative), and then applying it to arguments using apply-in, apply-as, or invoke-as. This abstraction allows for swapping out the underlying execution mechanism (e.g., local Clojure evaluation, remote execution, or execution within a transpiled language runtime) without changing the core application logic.
5.4 Example:
Example: example
;; Define a host applicative to add numbers
(def adder (host-applicative {:form '+}))
;; Apply it with arguments
(apply-in adder nil [1 2 3])
=> 6
;; Apply it, letting it resolve its own context (which is none for host-applicative)
(apply-as adder [10 20])
=> 30
;; Invoke it directly
(invoke-as adder 1 2 3 4 5)
=> 15
;; Asynchronous execution
@(invoke-as (host-applicative {:form '+ :async true}) 1 2 3)
=> 6
This module lays the groundwork for a flexible function application system, crucial for a multi-language and multi-runtime ecosystem like foundation-base.