factgraph

Knowledge graph for modeling facts, rules, and calculations in Kit

Files

FileDescription
.editorconfigEditor formatting configuration
.gitignoreGit ignore rules for build artifacts and dependencies
.tool-versionsasdf tool versions (Zig, Kit)
LICENSEMIT license file
README.mdThis file
examples/basic.kitBasic usage example
examples/fact-graph.kitExample: fact graph
kit.tomlPackage manifest with metadata and dependencies
src/main.kitMain module
tests/factgraph.test.kitTests for factgraph

Dependencies

No Kit package dependencies.

Installation

kit add gitlab.com/kit-lang/packages/kit-factgraph.git

Usage

import Kit.Factgraph as FactGraph

main = fn =>
  # Create a dictionary with metadata, type definitions, and fact definitions
  dict = FactGraph.empty-dictionary {name: "Person Schema", version: "1.0"}
    |> FactGraph.add-type "PositiveInt" (FactGraph.make-type "PositiveInt" "Int")
    |> FactGraph.add-fact "/person/name" (FactGraph.make-fact "String")
    |> FactGraph.add-fact "/person/age" (FactGraph.make-fact "PositiveInt")
    |> FactGraph.add-fact "/person/email" (FactGraph.make-optional-fact "String")

  # Create an empty fact graph from the dictionary
  graph = FactGraph.empty dict
    |> FactGraph.set "/person/name" "Alice"
    |> FactGraph.set "/person/age" 30

  # Read facts by path
  name = FactGraph.get graph "/person/name"
  age = FactGraph.get graph "/person/age"

  println "Name: ${name}"
  println "Age: ${age}"

  # Inspect paths and serialize the graph data
  println "All paths: ${FactGraph.paths graph}"
  println "Data: ${FactGraph.to-map graph}"

main

Developer Notes

kit-factgraph models data with three related structures:

  1. A dictionary created with FactGraph.empty-dictionary, then extended with type, fact, and calculation definitions.
  2. A graph created with FactGraph.empty, which stores dictionary metadata, graph data, and validation errors.
  3. Path-based data access with slash-delimited paths such as /person/age.

Useful exported operations include:

OperationPurpose
empty-dictionaryCreate a dictionary with caller-defined metadata
make-type / add-typeDefine named fact types
make-fact / make-optional-fact / add-factDefine required and optional facts
add-calculation / is-calculated?Register and inspect derived facts
split-pathSplit /a/b/c style paths into path segments
get / setRead and write graph values by path
get-at-path / set-at-pathRead and write nested Map values directly
pathsCollect paths currently present in graph data
dictionary / data / has-errors? / get-errorsInspect graph internals
to-map / from-mapConvert graph data to and from plain maps
is-valid-type? / is-valid-fact?Validate built-in and dictionary-backed fact types

Built-in validation recognizes Int, Integer, Float, String, Bool, and Boolean. Unknown type names currently pass validation so callers can layer domain-specific validation on top.

Some tests for nested writes and complex dictionary inserts are currently commented with TODOs for an OutOfMemory issue in recursive Map.insert usage. Keep this in mind when changing set-at-path, add-type, add-fact, or add-calculation.

Development

Running Examples

Run the basic example with the interpreter:

kit run examples/basic.kit

Run the larger fact graph demonstration:

kit run examples/fact-graph.kit

Compile examples to a native binary:

kit build examples/basic.kit && ./basic

Running Tests

Run the test suite:

kit test

Run the factgraph test file directly:

kit test tests/factgraph.test.kit

Run the test suite with coverage:

kit test --coverage

Running kit dev

Run the standard development workflow (format, check, test):

kit dev

This will:

  1. Format and check source files in src/
  2. Run tests in tests/ with coverage

Generating Documentation

Generate API documentation from doc comments:

kit doc

Note: Kit sources with doc comments (##) will generate HTML documents in docs/*.html.

Cleaning Build Artifacts

Remove generated files, caches, and build artifacts:

kit task clean

Note: Defined in kit.toml.

Local Installation

To install this package locally for development:

kit install

This installs the package to ~/.kit/packages/@kit/factgraph/, making it available for import as Kit.Factgraph in other projects.

License

This package is released under the MIT License - see LICENSE for details.

Exported Functions & Types

empty-dictionary

Create an empty fact dictionary with metadata

a -> {meta: a, types: Map, facts: Map, calcs: Map}

dict = FactGraph.empty-dictionary {name: "Tax Form", version: "1.0"}

empty

Create an empty fact graph with a dictionary

a -> {dict: a, data: Map, errors: List}

graph = FactGraph.empty dict

make-type

Create a type definition

NonEmptyString -> NonEmptyString -> TypeDef

int-type = FactGraph.make-type "PositiveInt" "Int"

add-type

Add a type definition to a dictionary

{meta: a, types: Map, facts: Map, calcs: Map} -> NonEmptyString -> TypeDef -> {meta: a, types: Map, facts: Map, calcs: Map}

dict = dict |> FactGraph.add-type "Money" (FactGraph.make-type "Money" "Float")

make-fact

Create a required fact definition

String -> FactDef

age-fact = FactGraph.make-fact "Int"

make-optional-fact

Create an optional fact

String -> FactDef

status-fact = FactGraph.make-optional-fact "String"

add-fact

Add a fact definition to a dictionary

{meta: a, types: Map, facts: Map, calcs: Map} -> NonEmptyString -> FactDef -> {meta: a, types: Map, facts: Map, calcs: Map}

dict = dict |> FactGraph.add-fact "/person/age" age-fact

add-calculation

Add a calculation (derived fact) to a dictionary

{meta: a, types: Map, facts: Map, calcs: Map} -> NonEmptyString -> (b -> c) -> {meta: a, types: Map, facts: Map, calcs: Map}

dict = dict |> FactGraph.add-calculation "/total" (fn(graph) => ...)

is-calculated?

Check if a path is a calculated fact

{calcs: Map, ..} -> NonEmptyString -> Bool

is-calc = FactGraph.is-calculated? dict "/total"

split-path

Split a path like "/person/age" into parts ["person", "age"]

String -> List String

parts = FactGraph.split-path "/person/age"
# parts = ["person", "age"]

get

Get a value from the fact graph at the given path

{data: Map, ..} -> NonEmptyString -> a

age = FactGraph.get graph "/person/age"

set

Set a value in the fact graph at the given path

{dict: a, data: Map, errors: List} -> NonEmptyString -> b -> {dict: a, data: Map, errors: List}

graph = FactGraph.set graph "/person/age" 30

get-at-path

Get a value at a nested path in a map

Map String a -> NonEmptyString -> a

set-at-path

Set a value at a nested path in a map

Map String a -> NonEmptyString -> a -> Map String a

paths

Get all paths in the fact graph data

{data: Map, ..} -> List String

all-paths = FactGraph.paths graph

dictionary

Get the dictionary from a fact graph

{dict: a, ..} -> a

dict = FactGraph.dictionary graph

data

Get the data map from a fact graph

{data: a, ..} -> a

data = FactGraph.data graph

has-errors?

Check if the fact graph has validation errors

{errors: List, ..} -> Bool

has-errors = FactGraph.has-errors? graph

get-errors

Get all validation errors from the fact graph

{errors: a, ..} -> a

errors = FactGraph.get-errors graph

to-map

Convert fact graph data to a plain map (for serialization)

{data: a, ..} -> a

map = FactGraph.to-map graph

from-map

Create a fact graph from a dictionary and a plain map

a -> Map -> {dict: a, data: Map, errors: List}

graph = FactGraph.from-map dict saved-data

is-valid-type?

Check if a value matches a type definition

String -> a -> Bool

valid = FactGraph.is-valid-type? "Int" 42

is-valid-fact?

Check if a fact value is valid against its definition in the graph

{dict: {facts: Map, ..}, ..} -> NonEmptyString -> a -> Bool

result = FactGraph.is-valid-fact? graph "/person/age" 30