Metastatic.Analysis.Purity (Metastatic v0.10.4)

View Source

Function purity analysis at the MetaAST level.

Analyzes code to determine if it's pure (no side effects) or impure (has side effects like I/O, mutations, random operations, etc.).

Works across all supported languages by operating on the unified MetaAST representation.

Purity Definition

A pure function:

  • Always returns the same output for the same input (deterministic)
  • Has no side effects (no I/O, no mutations, no global state access)
  • Doesn't depend on external state

An impure function has one or more of:

  • I/O operations (print, file access, network, database)
  • Mutations (modifying variables, especially in loops)
  • Non-deterministic operations (random, time/date)
  • Exception handling (raising/catching exceptions)

Usage

alias Metastatic.{Document, Analysis.Purity}

# Analyze a document
ast = {:binary_op, :arithmetic, :+, {:variable, "x"}, {:literal, :integer, 5}}
doc = Document.new(ast, :elixir)
{:ok, result} = Purity.analyze(doc)

result.pure?              # => true
result.effects            # => []
result.confidence         # => :high
result.summary            # => "Function is pure"

Examples

# Pure arithmetic
iex> ast = {:binary_op, [category: :arithmetic, operator: :+], [{:literal, [subtype: :integer], 1}, {:literal, [subtype: :integer], 2}]}
iex> doc = Metastatic.Document.new(ast, :python)
iex> {:ok, result} = Metastatic.Analysis.Purity.analyze(doc)
iex> result.pure?
true

# Impure I/O
iex> ast = {:function_call, [name: "print"], [{:literal, [subtype: :string], "hello"}]}
iex> doc = Metastatic.Document.new(ast, :python)
iex> {:ok, result} = Metastatic.Analysis.Purity.analyze(doc)
iex> result.pure?
false
iex> result.effects
[:io]

Summary

Functions

analyze(language_or_doc, source_or_ast_or_opts \\ [], opts \\ [])

@spec analyze(Metastatic.language(), term(), keyword()) ::
  {:ok, map()} | {:error, term()}

Analyzes a document for purity.

Accepts either:

Returns {:ok, result} where result is a Metastatic.Analysis.Purity.Result struct.

Examples

# Using Document
iex> ast = {:literal, [subtype: :integer], 42}
iex> doc = Metastatic.Document.new(ast, :elixir)
iex> {:ok, result} = Metastatic.Analysis.Purity.analyze(doc)
iex> result.pure?
true

# Using {language, native_ast} tuple
iex> python_ast = %{"_type" => "Constant", "value" => 42}
iex> {:ok, result} = Metastatic.Analysis.Purity.analyze(:python, python_ast)
iex> result.pure?
true

analyze!(language_or_doc, source_or_ast_or_opts \\ [], opts \\ [])

@spec analyze!(Metastatic.language(), term(), keyword()) :: map()

Analyzes a document for purity.

Accepts either:

Returns {:ok, result} where result is a Metastatic.Analysis.Purity.Result struct.

Examples

# Using Document
iex> ast = {:literal, [subtype: :integer], 42}
iex> doc = Metastatic.Document.new(ast, :elixir)
iex> {:ok, result} = Metastatic.Analysis.Purity.analyze(doc)
iex> result.pure?
true

# Using {language, native_ast} tuple
iex> python_ast = %{"_type" => "Constant", "value" => 42}
iex> {:ok, result} = Metastatic.Analysis.Purity.analyze(:python, python_ast)
iex> result.pure?
true

Unlike not-banged version, this one either returns a result or raises