Hy-Functional: Functional library for Hy

Inspired by Clojure.

Bootstrap

Require & Import all objects and macros of hy-functional directly.

(hy.I.hy-functional._bootstrap)

Macros

comment

(require
  hy-functional.core [comment])

(defn add [x y]
  (+ x y))

;; Ignore body when compile.
;; Writing some tests for repl or rct.
(comment
  (add 1 2) ; => 3
  (add 1 3) ; => 4
  )

ignore

(require
  hy-functional.core [ignore])

(defn log-error [msg]
  ;; Ignore body return value.
  (ignore
    (print msg)))

if-not when-not

(require
  hy-functional.core [if-not when-not])

(defn f [opts]
  ;; Reverse branch of if, short first for readability.
  (if-not opts
    (short-form)
    (long-form
      process-opts
      ...)))

(defn f [success?]
  ;; Reverse branch of when.
  (when-not success?
    (raise ...)))

def

(require
  hy-functional.core [def])

;; Alias of setv, used in top level.
(def x 1)
(require
  hy-functional.core [defarity])

;;; Clojure like fn def for varargs.
(defarity reduce
  ([f iterable]
    ...)
  ([f init iterable]
    ...))
(require
  hy-functional.core [-> ->>])

(import
  hy-functional.core [reduce inc even? assoc update])

;; For nested object transformation.
(-> {}
    (assoc "a" 1 "b" 2)
    (update "a" inc))

;; equiv: (update (assoc {} "a" 1 "a" 2) "a" inc)
;; => {"a" 2 "b" 2}

;; For nexted seq transformation.
(->> (range 4)
     (map inc)
     (filter even?)
     (reduce +))

;; equiv: (reduce + (filter even? (map inc (range 4))))
;; => 6
(require
  hy-functional.core [doto])

;; For impure object method calls.
(doto []
      (.append 1)
      (.extend [2 3]))

;; equiv: (let [$ []] (.append $ 1)  (.extend $ [2 3]) $)
(require
  hy-functional.core [cond-> cond->>])

(import
  hy-functinal.core [conj assoc])

;; Easy opts processing.

(defn f [opt1 opt2]
  (cond-> {}
    opt1 (assoc "opt1" ...)
    opt2 (assoc "opt2" ...)))

(defn g [opt1 opt2]
  (cond-> []
    opt1 (conj "opt1" ...)
    opt2 (conj "opt2" ...)))

loop

(require
  hy-functional.core [loop])

;; Clojure like loop.
(loop [i 0]
  (if (> i 10)
    i
    (recur (<< i 1))))
;; => 16

;; Loop with yield.
(loop [i 0]
  (if (> i 10)
    i
    (do
      (yield i)
      (recur (<< i 1)))))
;; => 1, 2, 4, 8, 16

Basic

;; None pred.
(none? None) ; => True
(none? []) ; => False
(some? None) ; => False
(some? []) ; => True

;; Int utils.
(inc 1) ; => 2
(dec 2) ; => 1
(zero? 0) ; => True
(pos? 1) ; => True
(neg? -1) ; => True
(even? 2) ; => True
(odd? 1) ; => True

Func

ignore

(defn f [callback]
  (let [res ...]
    (callback res)))

;; Accept any args, do nothing.
(f :callback ignore)

;; Cons a new fn like ignore with spec return value.
(f :callback (constantly :error))

comp

;; Return arg directly.
(identity 1) ; => 1

;; Unit of fn mult.
(identity (f x)) ; => (f x)

;; Fn mult.
((comp inc inc) 1) ; => 3
((comp inc inc inc) 1) ; => 4

partial

;; Partially bind args to fn.
(setv add-1-2-* (partial hy.pyops.+ 1 2))
(add-1-2-* 3) ; => 6

;; Curry the fn.
(setv add-1-*-* (curry 3 hy.pyops.+ 1))
(add-1-*-* 2 3) ; => 6
((add-1-*-* 2) 3) ; => 6

complement

(filter even? (range 6)) ; => 0, 2, 4

;; Reverse pred result.
(filter (complement even?) (range 6)) ; => 1, 3, 5

Iter

map filter reduce

;; Reduce seq to result by reducing fn: acc, input => acc.
(reduce hy.pyops.+ 1 (range 4)) ; => 10
(reduce hy.pyops.+ (range 4)) ; => 9

;; Reverse of filter.
(remove even? (range 6)) ; => 1, 3, 5

cat

;; Concat seqs.
(concat [1 2] [3 4]) ; => 1, 2, 3, 4
(concat [1] [2 3] [4]) ; => 1, 2, 3, 4

;; Concat seqs from seq, for undetermined (even infinite) inputs.
(cat [[1 2] [3 4]]) ; => 1, 2, 3, 4
(cat [[1] [2 3] [4]]) ; => 1, 2, 3, 4

;; Concat one object before seq.
(cons 1 [2 3]) ; => 1, 2, 3

;; Concat result of maps.
(mapcat range [1 2 3]) ; => 0, 0, 1, 0, 1, 2

first rest empty?

;; First of seq.
(first [1 2 3]) ; => 1
(first (range 3)) ; => 0

;; Rest of seq.
(rest [1 2 3]) ; => 2, 3
(rest (range 3)) ; => 1, 2

;; Is seq empty?
(empty? [1 2 3]) ; => False
(empty? (range 3)) ; => False
(empty? []) ; => True
(empty? (range 0)) ; => True

;; Second of seq.
(second [1 2]) ; => 2
(second (range 3)) ; => 1

;; Last of seq.
(last [1 2 3]) ; => 3

;; Seq without last.
(butlast [1 2 3]) ; => 2, 3

iterate

;; iterate fn on a init value.
(iterate inc 0) ; => 0, 1, 2, ...
(iterate dec 0) ; => 0, -1, -2, ...

;; Repeat value.
(repeat 1) ; => 1, 1, 1, ...

;; Repeat fn.
(repeatedly auto) ; => auto-0, auto-1, auto-2, ...

;; Cycle in seq.
(cycle [1 2 3]) ; => 1, 2, 3, 1, 2, 3, ...

interleave

;; Interleave shortest seqs.
(interleave [1 2 3] [4 5 6] [7 8 9 10]) ; => 1, 4, 7, 2, 5, 8, 3, 6, 9

;; Interpose sep to seq.
(interpose [0 [1 2 3]]) ; => 1, 0, 2, 0, 3
(interpose [0 [1]]) ; => 1,
(interpose [0 []]) ; => <empty>

Coll

conj into

;; Conjoin object to list.
(conj [1 2] 3) ; => [1 2 3]
(conj [1 2] 3 4) ; => [1 2 3 4]

;; Seqs into list.
(into [1 2] (range 3)) ; => [1 2 0 1 2]

assoc dissoc update merge

;; Assoc dict of k to v.
(assoc {"a" 1} "b" 2) ; => {"a" 1 "b" 2}
(assoc {"a" 1} "b" 2 "c" 3) ; => {"a" 1 "b" 2 "c" 3}

;; Dissoc dict of k.
(dissoc {"a" 1 "b" 2} "b") ; => {"a" 1}
(dissoc {"a" 1 "b" 2} "a" "b") ; => {}

;; Update dict of k with f, and optional additional args.
(update {"a" 1 "b" 2} "a" inc) ; => {"a" 2 "b" 2}
(update {"a" 1 "b" 2} "a" hy.pyops.+ 2) ; => {"a" 3 "b" 2}

;; Merge dicts to one dict.
(merge {"a" 1 "b" 2} {"b" 3 "c" 4} {"c" 5 "d" 6}) ; {"a" 1 "b" 3 "c" 5 "d" 6}

flatten

;; Coll: iterable and not str or bytes.
(coll? "hello") ; => False
(coll? b"hello") ; => False
(coll? [1 2 3]) ; => True
(coll? #{1 2 3}) ; => True
(coll? (range 3)) ; => True

;; Flatten nested seq.
(flatten [1 2 [3 4 #{5 6} "hello"] "world"]) ; => 1, 2, 3, 4, 5, 6, "hello", "world"

group-by

;; Group seqs by key fn.
(group-by even? (range 10)) ; => {True [0 2 4 6 8] False [1 3 5 7 9]}
(group-by (fn [x] (% x 3)) (range 10)) ; => {0 [0 3 6 9] 1 [1 4 7] [2 5 8]}

Seq

TODO