Runic.Component protocol (Runic v0.1.0-alpha.7)

Copy Markdown View Source

Protocol defining how Runic components compose together and connect within workflows.

The Component protocol supports extension of modeling new component types that can be added and connected with other components in Runic workflows. It provides introspection capabilities for components (sub-components, inputs, outputs) and connection semantics for workflow composition.

Protocol Functions

FunctionPurpose
connectable?/2Check if a component can be connected to another
connect/3Connect this component to a parent in a workflow
source/1Returns the source AST for building/serializing the component
hash/1Returns the content-addressable hash of the component
inputs/1Returns the nimble_options schema for component inputs
outputs/1Returns the nimble_options schema for component outputs

Built-in Implementations

Component TypeDescription
Runic.Workflow.StepSingle transformation function
Runic.Workflow.RuleConditional logic with condition and reaction
Runic.Workflow.MapFan-out transformation over enumerables
Runic.Workflow.ReduceFan-in aggregation
Runic.Workflow.AccumulatorStateful reducer across invocations
Runic.Workflow.StateMachineStateful reducer with reactive conditions
Runic.WorkflowWorkflows themselves are components
TuplePipeline syntax {parent, [children]}

Type Compatibility

The Component protocol includes type compatibility checking via an internal TypeCompatibility helper module. This enables schema-based validation when connecting components:

# Type compatibility checks
TypeCompatibility.types_compatible?(:any, :integer)  # => true
TypeCompatibility.types_compatible?(:string, :integer)  # => false

# Port compatibility for connecting components
producer_outputs = [out: [type: {:list, :integer}]]
consumer_inputs = [in: [type: {:list, :any}]]
TypeCompatibility.ports_compatible?(producer_outputs, consumer_inputs)  # => {:ok, :inferred}

Usage

require Runic

step = Runic.step(fn x -> x * 2 end, name: :double)
rule = Runic.rule(fn x when x > 10 -> :large end, name: :classify)

# Introspection
Runic.Component.hash(step)  # => content-addressable hash
Runic.Component.source(step)  # => AST representation

# Compatibility checking
Runic.Component.connectable?(step, rule)  # => true

# Connection (typically done via Workflow.add/3)
workflow = Runic.Workflow.new()
  |> Runic.Workflow.add(step)
  |> Runic.Workflow.add(rule, to: :double)

Implementing Custom Component

defmodule MyApp.CustomComponent do
  defstruct [:hash, :name, :config]
end

defimpl Runic.Component, for: MyApp.CustomComponent do
  alias Runic.Workflow

  def connectable?(_component, _other), do: true

  def connect(component, to, workflow) do
    workflow
    |> Workflow.add_step(to, some_internal_step(component))
    |> Workflow.register_component(component)
  end

  def source(component) do
    quote do
      MyApp.CustomComponent.new(name: unquote(component.name))
    end
  end

  def hash(component), do: component.hash

  def inputs(_component), do: [in: [type: :any, doc: "Input value"]]

  def outputs(_component), do: [out: [type: :any, doc: "Output value"]]
end

See the Protocols Guide for more details and examples.

Summary

Types

t()

All the types that implement this protocol.

Functions

Check if a component can be connected to another component.

Returns port contract for component inputs. Each entry is a named port with options like :type, :doc, :cardinality, :required.

Returns port contract for component outputs. Each entry is a named port with options like :type, :doc, :cardinality.

Returns the source AST for building a component.

Types

t()

@type t() :: term()

All the types that implement this protocol.

Functions

connect(component, to, workflow)

connectable?(component, other_component)

Check if a component can be connected to another component.

hash(component)

inputs(component)

Returns port contract for component inputs. Each entry is a named port with options like :type, :doc, :cardinality, :required.

outputs(component)

Returns port contract for component outputs. Each entry is a named port with options like :type, :doc, :cardinality.

source(component)

Returns the source AST for building a component.