The Frist Public Release of Hymn

I am happy to announce the first public release of hymn, a new monad library for Hy and Python. In addition to implementing many common monad types, with the help of lisp macro, it is possible to write monadic code in do notation, like so (example in Reader Monad):

=> (require hymn.dsl)
=> (import [hymn.types.reader [lookup]])
=> ;; do notation with reader monad, lookup assumes the environment is subscriptable
=> (def r (do-monad [a (lookup 'a) b (lookup 'b)] (+ a b)))
=> ;; run reader monad r with environment
=> (.run r {'a 1 'b 2})
3

I also wrote some interesting examples with it, just check out the examples directory, to get a taste of it, here is the example that using writer monad for logging:

(import [hymn.dsl [tell]])

(require hymn.dsl)

(defn gcd [a b]
  (if (zero? b)
    (do-monad
      [_ (tell (.format "the result is: {}\n" (abs a)))]
      (abs a))
    (do-monad-m
      [_ (tell (.format "{} mod {} = {}\n" a b (% a b)))]
      (gcd b (% a b)))))

(defmain [&rest args]
  (if (-> args len (!= 3))
    (print "usage:" (get args 0) "number1 number2")
    (let [[a (int (get args 1))]
          [b (int (get args 2))]]
      (print "calculating the greatest common divisor of" a "and" b)
      (print (.execute (gcd a b))))))

And here is the state monad example, using monte carlo method to calculate pi:

(import
  [collections [Counter]]
  [time [time]]
  [hymn.dsl [get-state replicate set-state]])

(require hymn.dsl)

;;; Knuth!
(def a 6364136223846793005)
(def c 1442695040888963407)
(def m (** 2 64))

;;; linear congruential generator
(def random
  (do-monad
    [seed get-state
     _ (set-state (-> seed (* a) (+ c) (% m)))
     new-seed get-state]
    (/ new-seed m)))

(def random-point (do-monad [x random y random] (, x y)))

(defn points [seed]
  "stream of random points"
  (while true
    ;; NOTE:
    ;; limited by the maximum recursion depth, we take 150 points each time
    (setv [random-points seed] (.run (replicate 150 random-point) seed))
    (for [point random-points]
      (yield point))))

(defn monte-carlo [number-of-points]
  "use monte carlo method to calculate value of pi"
  (def samples (take number-of-points (points (int (time)))))
  (def result
    (Counter (genexpr (>= 1.0 (+ (** x 2) (** y 2))) [[x y] samples])))
  (-> result (get true) (/ number-of-points) (* 4)))

(defmain [&rest args]
  (if (-> args len (!= 2))
    (print "usage:" (get args 0) "number-of-points")
    (print "the estimate for pi =" (-> args (get 1) int monte-carlo))))

This should replace the older monad library.