SLIPS

Practical Examples

Hello World

The classic first program in SLIPS:

import SLIPS

let env = CLIPS.createEnvironment()

CLIPS.eval(expr: """
(defrule hello
    =>
    (printout t "Hello, SLIPS!" crlf))
""")

CLIPS.run(limit: nil)

Fact Management

Template Definition

CLIPS.eval(expr: """
(deftemplate person
    (slot name (type STRING))
    (slot age (type INTEGER))
    (slot city (type STRING)))
""")

Assert and Retract

// Assert
let id1 = CLIPS.eval(expr: "(assert (person (name \"John\") (age 30) (city \"NYC\")))")
let id2 = CLIPS.eval(expr: "(assert (person (name \"Jane\") (age 25) (city \"LA\")))")

// List facts
CLIPS.eval(expr: "(facts)")

// Retract
CLIPS.eval(expr: "(retract \(id1))")

Production Rules

Simple Pattern Matching

CLIPS.eval(expr: """
(defrule adult
    (person (name ?n) (age ?a&:(>= ?a 18)))
    =>
    (printout t ?n " is an adult" crlf))
""")

Shared Variable Patterns

CLIPS.eval(expr: """
(defrule same-city
    (person (name ?n1) (city ?c))
    (person (name ?n2&~?n1) (city ?c))
    =>
    (printout t ?n1 " and " ?n2 " live in " ?c crlf))
""")

Negation (NOT)

CLIPS.eval(expr: """
(defrule no-orders
    (customer (id ?id) (name ?n))
    (not (order (customer-id ?id)))
    =>
    (printout t "Customer " ?n " has no orders" crlf))
""")

Complete Expert System

Simplified medical diagnosis system:

CLIPS.eval(expr: """
(deftemplate symptom
    (slot name)
    (slot severity (type INTEGER)))

(deftemplate diagnosis
    (slot disease)
    (slot probability (type FLOAT)))

(defrule flu
    (symptom (name fever) (severity ?s1&:(> ?s1 7)))
    (symptom (name cough))
    (symptom (name headache))
    =>
    (assert (diagnosis (disease flu) (probability 0.85)))
    (printout t "Probable flu (85%)" crlf))

(defrule cold
    (symptom (name cough))
    (symptom (name stuffy-nose))
    (not (symptom (name fever) (severity ?s&:(> ?s 7))))
    =>
    (assert (diagnosis (disease cold) (probability 0.75)))
    (printout t "Probable cold (75%)" crlf))
""")

// Enter symptoms
CLIPS.eval(expr: "(assert (symptom (name fever) (severity 8)))")
CLIPS.eval(expr: "(assert (symptom (name cough) (severity 6)))")
CLIPS.eval(expr: "(assert (symptom (name headache) (severity 5)))")

// Execute
CLIPS.run(limit: nil)

Control Flow

Salience (Priority)

CLIPS.eval(expr: """
(defrule urgent
    (salience 100)
    (alarm ?type)
    =>
    (printout t "URGENT: " ?type crlf))

(defrule normal
    (salience 0)
    (message ?msg)
    =>
    (printout t "Message: " ?msg crlf))
""")

Agenda Strategies

// Depth (default)
CLIPS.eval(expr: "(set-strategy depth)")

// Breadth
CLIPS.eval(expr: "(set-strategy breadth)")

// LEX (Recency)
CLIPS.eval(expr: "(set-strategy lex)")

Functions and Calculations

CLIPS.eval(expr: """
(defrule calculate-discount
    (order (id ?id) (amount ?amt))
    (customer (id ?cid) (type premium))
    =>
    (bind ?discount (* ?amt 0.20))
    (bind ?final (- ?amt ?discount))
    (printout t "Order " ?id ": $" ?final " (discount $" ?discount ")" crlf))
""")

Multifield

CLIPS.eval(expr: """
(deftemplate order
    (multislot products))

(defrule analyze-order
    (order (products $?items))
    =>
    (printout t "Product count: " (length$ $?items) crlf))
""")

Debug and Watch

// Enable watch facts
CLIPS.eval(expr: "(watch facts)")

// Enable watch rules
CLIPS.eval(expr: "(watch rules)")

// Output:
// ==> (person (name "John") (age 30))
// ==> Activation adult
// FIRE adult

// Disable
CLIPS.eval(expr: "(unwatch facts)")
CLIPS.eval(expr: "(unwatch rules)")

Template Functions ๐Ÿ†•

Modify and Duplicate

// Define template
CLIPS.eval(expr: """
(deftemplate person
    (slot name)
    (slot age)
    (slot city))
""")

// Create fact
CLIPS.eval(expr: "(assert (person (name \"John\") (age 30) (city \"Rome\")))")
// ==> f-1

// Modify existing fact
CLIPS.eval(expr: "(modify 1 (age 31) (city \"Milan\"))")
// ==> f-2 (new fact with modifications)

// Duplicate with modifications
CLIPS.eval(expr: "(duplicate 1 (name \"Jane\"))")
// ==> f-3 (Jane, 31, Milan)

Introspection

// List all slots of a template
CLIPS.eval(expr: "(deftemplate-slot-names person)")
// Output: (create$ name age city)

// Check if a slot is multifield
CLIPS.eval(expr: "(deftemplate-slot-multip person name)")
// Output: FALSE

// Check slot existence
CLIPS.eval(expr: "(deftemplate-slot-existp person salary)")
// Output: FALSE

Modules and Focus ๐Ÿ†•

// Define modules
CLIPS.eval(expr: """
(defmodule BILLING
    (export ?ALL))

(defmodule SHIPPING
    (export ?ALL))

(defmodule MAIN
    (import BILLING ?ALL)
    (import SHIPPING ?ALL))
""")

// Define rules in different modules
CLIPS.eval(expr: """
(defmodule BILLING)
(defrule calculate-total
    (declare (salience 10))
    (order (amount ?a))
    =>
    (printout t "Total: $" ?a crlf))

(defmodule SHIPPING)
(defrule prepare-shipment
    (order (id ?id))
    =>
    (printout t "Preparing shipment for order " ?id crlf))
""")

// Set focus to control execution order
CLIPS.eval(expr: "(focus BILLING SHIPPING)")

// Run will execute BILLING first, then SHIPPING
CLIPS.run(limit: nil)

Advanced Patterns

Complex Predicate Tests

CLIPS.eval(expr: """
(defrule suspicious-price
    (product (name ?n) (price ?p))
    (test (or (< ?p 0) (> ?p 10000)))
    =>
    (printout t "ALERT: " ?n " has anomalous price: $" ?p crlf))
""")

Wildcard Patterns

CLIPS.eval(expr: """
(defrule any-age
    (person (name ?n) (age ?))
    =>
    (printout t "Found person: " ?n crlf))
""")

Swift Integration

Custom Functions

import SLIPS

// Register custom function
var env = CLIPS.createEnvironment()
env.functionTable["my-func"] = FunctionDefinitionSwift(name: "my-func") { env, args in
    guard let val = args.first, case .int(let i) = val else {
        return .none
    }
    return .int(i * 2)
}

CLIPS.eval(expr: """
(defrule test
    =>
    (printout t "Result: " (my-func 21) crlf))
""")

CLIPS.run(limit: nil)  // Output: Result: 42

Custom Routers

var logs: [String] = []

Router.AddCallback(&env, name: "custom", priority: 10) { _, _, text in
    logs.append(text)
    return true
}

CLIPS.eval(expr: "(printout custom \"Log message\" crlf)")
print(logs)  // ["Log message\n"]