                [cats.context :as ctx]
                [cats.core :as m])))
 
+;; Generators
+
+(defn justs-of
+  [g]
+  (gen/fmap maybe/just g))
+
+(def just-gen
+  (justs-of gen/any))
+
+(def nothing-gen
+  (gen/return (maybe/nothing)))
+
+(def maybe-gen
+  (gen/one-of [just-gen nothing-gen]))
+
+(def vectors-gen
+  (gen/vector gen/any))
+
+;; Semigroup
+
+(defspec maybe-semigroup 10
+  (lt/semigroup-associativity {:ctx maybe/context
+                               :gen (justs-of (gen/not-empty vectors-gen))}))
+
+;; Monoid
+
+(defspec maybe-monoid 10
+  (lt/monoid-identity-element {:ctx maybe/context
+                               :gen (justs-of (gen/not-empty vectors-gen))
+                               :empty (maybe/just [])}))
+
+;; Functor
+
+(defspec maybe-first-functor-law 10
+  (lt/first-functor-law {:gen maybe-gen}))
+
+(defspec maybe-second-functor-law 10
+  (lt/second-functor-law {:gen maybe-gen
+                          :f str
+                          :g count}))
+
+;; Applicative
+
+(defspec maybe-applicative-identity 10
+  (lt/applicative-identity-law {:ctx maybe/context
+                                :gen maybe-gen}))
+
+(defspec maybe-applicative-homomorphism 10
+  (lt/applicative-homomorphism {:ctx maybe/context
+                                :gen gen/any
+                                :f (constantly false)}))
+
+(defspec maybe-applicative-interchange 10
+  (lt/applicative-interchange {:ctx maybe/context
+                               :gen gen/int
+                               :appf (maybe/just inc)}))
+
+(defspec maybe-applicative-composition 10
+  (lt/applicative-composition {:ctx maybe/context
+                               :gen gen/int
+                               :appf (maybe/just inc)
+                               :appg (maybe/just dec)}))
+
+;; Monad
+
+(defspec maybe-first-monad-law 10
+  (lt/first-monad-law {:ctx maybe/context
+                       :mf #(if % (maybe/just %) (maybe/nothing))}))
+
+(defspec maybe-second-monad-law 10
+  (lt/second-monad-law {:ctx maybe/context}))
+
+(defspec maybe-third-monad-law 10
+  (lt/third-monad-law {:ctx maybe/context
+                       :f (comp maybe/just str)
+                       :g (comp maybe/just count)}))
+
+;; MonadPlus
+
+(defspec maybe-monadplus 10
+  (lt/monadplus-associativity {:ctx maybe/context
+                               :gen (gen/not-empty vectors-gen)}))
+
+;; MonadZero
+
+(defspec maybe-monadzero-identity 10
+  (lt/monadzero-identity-element {:ctx maybe/context
+                                  :gen (justs-of (gen/not-empty vectors-gen))}))
+
+(defspec maybe-monadzero-bind 10
+  (lt/monadzero-bind {:ctx maybe/context
+                      :gen just-gen}))
+
+;; Examples
+
 (t/deftest maybe-monad-tests
   (t/testing "Basic maybe operations."
     (t/is (= 1 (maybe/from-maybe (maybe/just 1))))

  (t/testing "Test predicates"
    (let [m1 (maybe/just 1)]
      (t/is (maybe/maybe? m1))
      (t/is (maybe/just? m1))))

  (t/testing "Test fmap"
    (let [m1 (maybe/just 1)
          m2 (maybe/nothing)]
      (t/is (= (m/fmap inc m1) (maybe/just 2)))
      (t/is (= (m/fmap inc m2) (maybe/nothing)))))

  (t/testing "Forms a Semigroup when its values form a Semigroup"
    (t/is (= (maybe/just [1 2 3 4 5])
             (m/mappend (maybe/just [1 2 3]) (maybe/just [4 5])))))

  (t/testing "Its identity element is Nothing"
    (t/is (= (maybe/nothing)
             (ctx/with-context maybe/context
               (m/mempty)))))

  (t/testing "The first monad law: left identity"
    (t/is (= (maybe/just 2)
             (m/>>= (m/return maybe/context 2) maybe/just))))

  (t/testing "The second monad law: right identity"
    (t/is (= (maybe/just 2)
             (m/>>= (maybe/just 2) m/return))))

  (t/testing "The third monad law: associativity"
    (t/is (= (m/>>= (m/mlet [x  (maybe/just 2)
                             y  (maybe/just (inc x))]
                      (m/return y))
                    (fn [y] (maybe/just (inc y))))
             (m/>>= (maybe/just 2)
                    (fn [x] (m/>>= (maybe/just (inc x))
                                   (fn [y] (maybe/just (inc y))))))))))

(def maybe-vector-t (maybe/maybe-t b/vector-context))


(ns cats.monad.maybe-spec
  #?@(:cljs
      [(:require [cljs.test :as t]
                 [clojure.test.check]
                 [clojure.test.check.generators :as gen]
                 [clojure.test.check.properties :as prop :include-macros true]
                 [cats.labs.test :as lt]
                 [cats.builtin :as b]
                 [cats.protocols :as p]
                 [cats.monad.maybe :as maybe]
                 [cats.monad.either :as either]
                 [cats.context :as ctx :include-macros true]
                 [cats.core :as m :include-macros true])
       (:require-macros [clojure.test.check.clojure-test :refer (defspec)])])
  #?(:clj
     (:require [clojure.test :as t]
               [clojure.test.check.clojure-test :refer [defspec]]
               [clojure.test.check :as tc]
               [clojure.test.check.generators :as gen]
               [clojure.test.check.properties :as prop]
               [cats.labs.test :as lt]
               [cats.builtin :as b]
               [cats.protocols :as p]
               [cats.monad.maybe :as maybe]
