Skip to content

Instantly share code, notes, and snippets.

@sritchie
Last active December 27, 2015 23:29
Show Gist options
  • Save sritchie/7406280 to your computer and use it in GitHub Desktop.
Save sritchie/7406280 to your computer and use it in GitHub Desktop.
(ns paddleguru.algebra
"Shared algebras for cljs and clj.")
(defprotocol Semigroup
(plus [l r]))
;; Shared extensions
(extend-protocol Semigroup
nil
(plus [l r] r))
;; Clojure-specific extension
#+clj
(extend-protocol Semigroup
String
(plus [l r] (str l r))
java.lang.Long
(plus [l r] (+ l r))
clojure.lang.Ratio
(plus [l r] (+ l r))
clojure.lang.IPersistentVector
(plus [l r] (concat l r))
clojure.lang.IPersistentList
(plus [l r] (concat l r))
clojure.lang.IPersistentMap
(plus [l r]
(merge-with plus l r)))
(defn monoid [zero]
(fn
([] zero)
([l r] (plus l r))))
(def numeric-monoid
(monoid 0))
(ns paddleguru.algebra-test
(:require [clojure.test :refer :all]
[paddleguru.algebra :as a]
[simple-check.core :as sc]
[simple-check.generators :as gen]
[simple-check.properties :as prop :refer (for-all)]))
(defn test-property
([p] (test-property 100 p))
([num-tests p]
(is (:result (sc/quick-check num-tests p)))))
(defn associative?
"Returns a property that tests that the supplied function is
associative for values produced by the supplied generator."
[generator f]
(prop/for-all [x generator
y generator
z generator]
(= (f (f x y) z)
(f x (f y z)))))
(defn semigroup-law
"Tests that the supplied generator produces values that can add
together with paddleguru.algebra/plus."
[type-name generator]
(testing (str type-name " forms a semigroup.")
(test-property
(associative? generator a/plus))))
(defn identity?
"Returns a property that checks that the supplied zero is an
identity for the supplied generator, when passed in to the supplied
function."
[generator f zero]
(prop/for-all [x generator]
(and (= (f x zero) x)
(= (f zero x) x))))
(defn monoid-laws
"Tests that the supplied monoid is both associative (in the 2-arg
case) and produces an identity in the no-arg case."
[type-name generator monoid]
(let [zero (monoid)]
(semigroup-law type-name generator)
(testing (format "%s and %s form a monoid." type-name zero)
(test-property
(identity? generator a/plus zero)))))
(deftest numeric-monoid-laws
;; Checks that Clojure's numeric types work monoid style.
(monoid-laws "int" gen/int a/numeric-monoid)
(monoid-laws "ratio" gen/ratio a/numeric-monoid))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment