Skip to content

Commit

Permalink
✅ [bare-state] Add tests
Browse files Browse the repository at this point in the history
Problem:
- The tests for the bare state monad are not sufficient.

Solution:
- Add additional tests
  • Loading branch information
sabjohnso committed Nov 6, 2024
1 parent 5c58f17 commit b09fb86
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 4 deletions.
20 changes: 18 additions & 2 deletions src/bare-state.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
(:nicknames :bs)
(:use :cl :contextual :contextual-derivation)
(:export
#:bs-run
#:bs-run #:bs-exec #:bs-eval
#:bs-fmap
#:bs-pure #:bs-fapply #:bs-product
#:bs-mreturn #:bs-flatmap #:bs-flatten
#:bs-mreturn #:bs-flatmap #:bs-bind #:bs-flatten
#:bs-mget #:bs-mput #:bs-select #:bs-modify
#:let*-fun/bs #:let-fun/bs
#:let-app/bs
Expand All @@ -23,6 +23,22 @@ returns the result of calling `MX' with `S'."
(declare (type function mx))
(funcall mx s))


(defun bs-exec (s mx)
"Return the value from the running the the input stateful
calculation, `MX' with the initial state `S'"

(declare (type function mx))
(car (bs-run s mx)))

(defun bs-eval (s mx)
"Return the final state from the running the the input stateful
calculation, `MX' with the initial state `S', ignoring the final value."

(declare (type function mx))
(cadr (bs-run s mx)))


(defun bs-mreturn (x)
"Wraps a value `X' in a state monad: returns a function that, when
given a state `S', returns `X' alongside `S' in a 2-elment list."
Expand Down
46 changes: 44 additions & 2 deletions test/bare-state-test.lisp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(in-package :cl-user)

(defpackage :contextual-bare-state-test
(:use :cl :fiveam :contextual :contextual-bare-state)
(:use :cl :fiveam :trivia :contextual :contextual-bare-state)
(:export #:run-all-tests!))

(in-package :contextual-bare-state-test)
Expand All @@ -27,7 +27,18 @@
(is (equal '(s s) (bs-run 's (bs-mget))))
(is (equal '("S" s) (bs-run 's (bs-select #'symbol-name))))
(is (equal '(nil s1) (bs-run 's0 (bs-mput 's1))))
(is (equal '(nil "S") (bs-run 's (bs-modify #'symbol-name)))))
(is (equal '(nil "S") (bs-run 's (bs-modify #'symbol-name))))
(let ((s0 '((x . 1) (y . 2))))
(flet ((by-key (k) (lambda (s) (cdr (assoc k s0)))))
(is (equal (list 3 s0)
(bs-run s0
(bs-bind
(bs-select (by-key 'x))
(lambda (x)
(bs-bind
(bs-select (by-key 'y))
(lambda (y)
(bs-mreturn (+ x y))))))))))))


(defmacro show (expr)
Expand Down Expand Up @@ -105,3 +116,34 @@
(let-app/bs ((x (bs-select (by-key 'x)))
(y (bs-select (by-key 'y))))
(+ x y)))))))))


;; Update the state of a game base on the input. The state is a tripple
;; with a flag indicating if it is `ON' or `OFF' followed by the score
;; for `A' and followed by the score for `B'. Valid inputs are one of
;; the symbol `A', `B', `ON' or `OFF'. Invalid inputs are just ignored
;; and the state is
(defun update-state (input)
(lambda (state)
(match (list input state)
((list 'on (list 'off a b)) (list 'on a b))
((list 'off (list 'on a b)) (list 'off a b))
((list 'a (list 'on a b)) (list 'on (1+ a) b))
((list 'b (list 'on a b)) (list 'on a (1+ b)))
((list _ state) (format t "~s" `(:input ,input :state ,state)) state))))

;; Construct the stateful computation of a game based on the input listn
(defun game (inputs)
(declare (type list inputs))
(if (null inputs) (bs-mreturn nil)
(destructuring-bind (input . more-inputs) inputs
(progn-mon/bs
(bs-modify (update-state input))
(game more-inputs)))))

(test game
(let ((initial-state '(off 0 0)))
(is (eq nil (bs-exec initial-state (game '(on)))))
(is (equal '(on 0 0) (bs-eval initial-state (game '(on)))))
(is (equal '(off 1 0) (bs-eval initial-state (game '(on a off)))))
(is (equal '(off 2 1) (bs-eval initial-state (game '(on a b a off)))))))

0 comments on commit b09fb86

Please sign in to comment.