198 lines
6.2 KiB
Common Lisp
198 lines
6.2 KiB
Common Lisp
;;;; cl-scopes/csys - concurrent cybernetic communication systems
|
|
|
|
(defpackage :scopes/csys
|
|
(:use :common-lisp)
|
|
(:local-nicknames (:actor :scopes/core/actor)
|
|
(:config :scopes/config)
|
|
(:message :scopes/core/message)
|
|
(:shape :scopes/shape)
|
|
(:util :scopes/util)
|
|
(:alx :alexandria))
|
|
(:export #:scope #:environ
|
|
#:make-program
|
|
#:neuron #:std-proc #:eff-proc #:create-zero
|
|
#:handle-action
|
|
#:notify
|
|
#:basic-actions #:no-op #:value-add #:create
|
|
#:multiply))
|
|
|
|
(in-package :scopes/csys)
|
|
|
|
;;;; scope: information a neuron has access to
|
|
|
|
(defclass scope ()
|
|
((value :accessor value :initarg :value :initform 0)
|
|
(categ :accessor categ :initarg :categ :initform '(:csys :c00))
|
|
(stage :accessor stage :initarg :stage :initform :initial)
|
|
(proc :accessor proc)
|
|
(program :reader program :initarg :program)
|
|
(syns :accessor syns :initform nil)
|
|
(environ :reader environ :initarg :environ)))
|
|
|
|
(defun scope (prg env &rest args &key (cls 'scope) &allow-other-keys)
|
|
(setf args (alx:remove-from-plist args :cls :environ :program))
|
|
(apply #'make-instance cls :program prg :environ env args))
|
|
|
|
(defmethod initialize-instance :after ((sc scope) &key &allow-other-keys)
|
|
(set-proc sc))
|
|
|
|
(defun reset-scope (scp &rest args)
|
|
(let* ((addr (getf args :addr))
|
|
(categ (if addr
|
|
(list (car addr) (cadr addr))
|
|
(getf args :categ (categ scp)))))
|
|
(setf args (alx:remove-from-plist args :categ :addr))
|
|
(apply #'scope
|
|
(getf args :program (program scp))
|
|
(getf args :environ (environ scp))
|
|
:categ categ
|
|
args)))
|
|
|
|
(defun set-proc (scope)
|
|
(setf (proc scope) (funcall (program scope) scope))
|
|
scope)
|
|
|
|
(defgeneric make-program (spec)
|
|
(:method ((proc function)) (lambda (scope) proc))
|
|
(:method ((spec list))
|
|
(make-program (alx:plist-hash-table spec :test #'equal)))
|
|
(:method ((spec hash-table))
|
|
(lambda (scope)
|
|
(let* ((cat (cadr (categ scope)))
|
|
(stg (stage scope))
|
|
(proc (gethash (list cat stg) spec
|
|
(gethash (list cat :default) spec
|
|
(gethash (list :default stg) spec
|
|
(gethash :default spec))))))
|
|
(unless proc
|
|
(util:lgw "proc not found" cat stg spec))
|
|
proc))))
|
|
|
|
(defun create-zero (prg env &key (categ '(:csys :c00)) (name "0-0"))
|
|
(let* ((domain (car categ))
|
|
(addr (list domain (cadr categ) name))
|
|
(msg (message:create (list domain :create) :data (list :addr addr)))
|
|
(scope (scope prg env :categ categ))
|
|
(zero (neuron scope)))
|
|
(notify-created msg zero scope)))
|
|
|
|
;;;; neurons (= async tasks) and synapses (= connections)
|
|
|
|
(defun neuron (scope)
|
|
(actor:create (process scope)))
|
|
|
|
(defun update (scope)
|
|
(actor:become (process scope)))
|
|
|
|
(defun synapse (rcvr &optional op)
|
|
(lambda (msg)
|
|
(let ((nmsg (if op (funcall op msg) msg)))
|
|
(when nmsg
|
|
(actor:send rcvr nmsg)))))
|
|
|
|
;;;; message handlers, proc steps
|
|
|
|
(defun process (scope)
|
|
(lambda (msg)
|
|
(funcall (proc scope) msg scope)))
|
|
|
|
(defun std-proc (&key (default (remember)) actions)
|
|
(lambda (msg scope)
|
|
(util:mv-bind (nmsg (nscope scope))
|
|
(handle-action msg scope :default default :actions actions)
|
|
(when nmsg (forward nmsg (syns nscope)))
|
|
(update nscope))))
|
|
|
|
(defun eff-proc (&key actions)
|
|
(lambda (msg scope)
|
|
(util:mv-bind (nmsg (nscope scope))
|
|
(handle-action msg scope :default (no-op) :actions actions)
|
|
(when nmsg
|
|
(setf (nth 1 (shape:head nmsg)) :effect)
|
|
(notify nmsg nscope))
|
|
(update nscope))))
|
|
|
|
(defun handle-action (msg scope &key (default (no-op)) actions)
|
|
(let* ((key (message:action msg))
|
|
(act (getf actions key (getf actions :default default))))
|
|
(funcall act msg scope)))
|
|
|
|
(defun forward (msg syns)
|
|
(dolist (s syns)
|
|
(funcall s msg)))
|
|
|
|
(defun notify (msg scope)
|
|
(actor:send (environ scope) msg))
|
|
|
|
(defun notify-created (msg new scope)
|
|
(let* ((head (list (message:domain msg) :created))
|
|
(data (util:plist-merge (shape:data msg) `(:new ,new :parent ,actor:*self*)))
|
|
(msg1 (message:create head :data data)))
|
|
(notify msg1 scope)))
|
|
|
|
;;;; action handlers
|
|
|
|
(defun basic-actions ()
|
|
(list :value (value-add)
|
|
:create (create)
|
|
:connect (connect)
|
|
:next (no-op :no-forward t)))
|
|
|
|
(defun no-op (&key no-forward)
|
|
(lambda (msg scope)
|
|
(unless no-forward msg)))
|
|
|
|
(defun remember ()
|
|
(lambda (msg scope)
|
|
(setf (value scope) (shape:data-value msg :value))
|
|
(values msg scope)))
|
|
|
|
(defun value-add (&key (bias 0) (threshold 0))
|
|
(lambda (msg scope)
|
|
(let* ((val (getf (shape:data msg) :value))
|
|
(newval (+ val (value scope))))
|
|
(if (>= newval threshold)
|
|
(let* ((head (new-msg-head msg scope))
|
|
(msg (message:create head :data (list :value newval))))
|
|
(setf (value scope) bias)
|
|
(values msg scope))
|
|
(progn
|
|
(setf (value scope) newval)
|
|
(values nil scope))))))
|
|
|
|
(defun create ()
|
|
(lambda (msg scope)
|
|
(let* ((data (shape:data msg))
|
|
(new (neuron (apply #'reset-scope scope data))))
|
|
(notify-created msg new scope)
|
|
(case (getf data :connect)
|
|
(:succ (send-connect actor:*self* new msg))
|
|
(:pred (send-connect new actor:*self* msg)))
|
|
nil)))
|
|
|
|
(defun connect ()
|
|
(lambda (msg scope)
|
|
(let ((data (shape:data msg)))
|
|
(setf (syns scope)
|
|
(cons (synapse (getf data :target) (getf data :op)) (syns scope)))
|
|
(values nil scope))))
|
|
|
|
;;;; synapse ops
|
|
|
|
(defun multiply (n)
|
|
(lambda (msg)
|
|
(let* ((data (copy-list (shape:data msg))))
|
|
(setf (getf data :value) (* (getf data :value 0) n))
|
|
(message:create (shape:head msg) :data data))))
|
|
|
|
;;;; helpers
|
|
|
|
(defun new-msg-head (msg scope)
|
|
(list (message:domain msg) (message:action msg) (cadr (categ scope))))
|
|
|
|
(defun send-connect (rcvr target msg)
|
|
(let ((data (shape:data msg)))
|
|
(actor:send rcvr
|
|
(message:create (list (message:domain msg) :connect)
|
|
:data (list :target target
|
|
:op (getf data :op))))))
|