<commit_before>(ns asid.error-flow
  (:use midje.sweet)

  (:require [clojure.algo.monads :as monad]))

(defrecord Failure [status message])

(defprotocol Failed
  (has-failed? [failure]))

(extend-protocol Failed
  Object
  (has-failed? [failure] false)

  Failure
  (has-failed? [failure] true)

  Exception
  (has-failed? [failure] true))

(monad/defmonad failure-m
  [m-result identity
   m-bind (fn [m f]
            (if (has-failed? m)
              m
              (f m)))])

(defn add-monadic-expr [insert form]
  (list 'm-bind insert (if (seq? form)
                         `(fn [bound#] (~(first form) bound# ~@(rest form)))
                         `(fn [bound#] (~form bound#)))))

(defmacro f->
  ([x]
     `(monad/with-monad failure-m ~x))
  ([x form]
     `(monad/with-monad failure-m
        ~(add-monadic-expr x form)))
  ([x form & more]
     `(f-> (f-> ~x ~form) ~@more)))

(defn- succeed [v]
  v)
(defn- include [m k v]
  (conj m [k v]))
(defn- show [m k]
  (get m k))
(defn- fail [m limit]
  (if (> limit (:limit m))
    (Failure. 400 "Imposed limit exceeded")
    m))

(fact
  (f-> 10 (+ 9)) => 19
  (f-> {} succeed) => {}
  (f-> 10 (+ 9) (- 11)) => 8)

(defn- short-steps [limit]
  (f-> {:limit 50}
       (fail limit)))

(fact
  (short-steps 10) => {:limit 50}
  (short-steps 100) => (partial instance? Failure))

(defn- more-steps [limit]
  (f-> {}
       succeed
       (include :limit 50)
       (fail limit)
       (include :value 100)
       (show :value)))

(fact
  (more-steps 10) => 100
  (more-steps 100) => (partial instance? Failure))<commit_msg>Generalisation of the thread macro to all monads.<commit_after>(ns asid.error-flow
  (:use midje.sweet)

  (:require [clojure.algo.monads :as monad]))

(defrecord Failure [status message])

(defprotocol Failed
  (has-failed? [failure]))

(extend-protocol Failed
  Object
  (has-failed? [failure] false)

  Failure
  (has-failed? [failure] true)

  Exception
  (has-failed? [failure] true))

(monad/defmonad failure-m
  [m-result identity
   m-bind (fn [m f]
            (if (has-failed? m)
              m
              (f m)))])

(defn bind-monadic-expr-into-form [insert form]
  (list 'm-bind insert (if (seq? form)
                         `(fn [bound#] (~(first form) bound# ~@(rest form)))
                         `(fn [bound#] (~form bound#)))))

(defmacro m->
  ([m x]
     `(monad/with-monad ~m ~x))
  ([m x form]
     `(monad/with-monad ~m
        ~(bind-monadic-expr-into-form x form)))
  ([m x form & more]
     `(m-> ~m (m-> ~m ~x ~form) ~@more)))

(defmacro fail->
  ([x] `(m-> ~failure-m ~x))
  ([x form] `(m-> failure-m ~x ~form))
  ([x form & more] `(fail-> (fail-> ~x ~form) ~@more)))

(defn- succeed [v]
  v)
(defn- include [m k v]
  (conj m [k v]))
(defn- show [m k]
  (get m k))
(defn- fail [m limit]
  (if (> limit (:limit m))
    (Failure. 400 "Imposed limit exceeded")
    m))

(fact
  (fail-> 10 (+ 9)) => 19
  (fail-> {} succeed) => {}
  (fail-> 10 (+ 9) (- 11)) => 8)

(defn- short-steps [limit]
  (fail-> {:limit 50}
          (fail limit)))

(fact
  (short-steps 10) => {:limit 50}
  (short-steps 100) => (partial instance? Failure))

(defn- more-steps [limit]
  (fail-> {}
          succeed
          (include :limit 50)
          (fail limit)
          (include :value 100)
          (show :value)))

(fact
  (more-steps 10) => 100
  (more-steps 100) => (partial instance? Failure))
{'fraction_non_alphanumeric': 0.1946980854197349, 'fraction_numerical': 0.021207658321060384, 'mean_word_length': 2.3590504451038576, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 40, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3065287', 'n_tokens_mistral': 1406, 'n_tokens_neox': 1277, 'n_words': 379}