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.



apply-as ^

[app args]
Added 3.0

allows the applicative to auto-resolve its context

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

apply-in ^

[app rt args]
Added 3.0

runs the applicative within a context

v 3.0
(defn apply-in
  ([app rt args]
   (let [input  (protocol.apply/-transform-in app rt args)
         output (protocol.apply/-apply-in app rt input)]
     (r/return-chain output (partial protocol.apply/-transform-out app rt args)))))
link
(apply-in (host-applicative {:form '+}) nil [1 2 3 4 5]) => 15

invoke-as ^

[app & args]
Added 3.0

invokes the applicative to args

v 3.0
(defn invoke-as
  ([app & args]
   (apply-as app args)))
link
(invoke-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.



host-applicative ^

[{:keys [function form async], :as m}]
Added 3.0

constructs an applicative that does not need a context

v 3.0
(defn host-applicative
  ([{:keys [function form async] :as m}]
   (map->HostApplicative m)))
link
@((host-applicative {:form '+ :async true}) 1 2 3 4 5) => 15

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 the std.protocol.apply/IApplicable protocol.n IApplicable Protocol: 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)n apply-as:n Purpose: Allows an applicative to automatically resolve its runtime context (either from its own :runtime field or by calling -apply-default).n Usage: (apply-as app args)n invoke-as:n Purpose: A convenience function that invokes an applicative with a variable number of arguments, internally calling apply-as.n Usage: (invoke-as app & args)n host-applicative:n Purpose: Constructs a basic HostApplicative record that can execute a Clojure function or form directly in the current host environment. It can optionally execute asynchronously using std.lib.future.n Usage: (host-applicative {:form '+ :async true})n HostApplicative (defimpl record):n Purpose: The concrete implementation of IApplicable for the host environment. It takes function, form, and async as fields.n Protocol Implementation: Its -apply-default, -transform-in, and -transform-out methods are simple pass-throughs or nil for the host context, as no special transformations or default runtimes are needed.n host-apply-in:n Purpose: A helper function used by HostApplicative to 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.