From 437995e63ba28f979ac9830f781fce2409bc3a6c Mon Sep 17 00:00:00 2001 From: Ag Ibragimov Date: Mon, 12 Aug 2024 13:20:15 -0500 Subject: [PATCH 1/6] improves (map) Our previous implementation of (map) apparently would leave around a GCiable structures that for some reason would hang around, in rare cases causing memory-leaks. This bug, I think, usually manifests itself on multi-monitor setups. What I used to observe: for no apparent reason Hammerspoon hangs, and the system would "swallow" certain keys - e.g., "a", "w", "j" - the keys on the main modal. Which consequently makes it difficult to type something like `"killall Hammerspoon"` in the terminal, and one has to use ActivityMonitor to kill and restart Hammerspoon, the tray icon wouldn't work either. This refactoring also makes (map) similar to Clojure function, you can feed multiple collections (tables) into it. --- lib/functional.fnl | 46 ++++++++++++++++++++++++++++++++++++++-------- windows.fnl | 10 +++++++--- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/lib/functional.fnl b/lib/functional.fnl index 967ce22..55bd92f 100644 --- a/lib/functional.fnl +++ b/lib/functional.fnl @@ -125,6 +125,10 @@ (set ct (+ ct 1)))) ct) +(fn apply [f ...] + (let [args [...]] + (f (table.unpack args)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Reduce Primitives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -145,6 +149,11 @@ k v (seq tbl)] (f acc v k))) +(fn concat [...] + (reduce (fn [cat tbl] + (each [_ v (ipairs tbl)] + (table.insert cat v)) + cat) [] [...])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Reducers @@ -162,14 +171,34 @@ tbl paths)) -(fn map - [f tbl] - (reduce - (fn [new-tbl v k] - (table.insert new-tbl (f v k)) - new-tbl) - [] - tbl)) +(fn zip [...] + "Groups corresponding elements from multiple lists into a new list, truncating at the length of the smallest list." + (let [tbls [...] + result []] + (if (= 1 (length tbls)) + (table.insert result (. tbls 1)) + (let [] + (for [idx 1 (length (. tbls 1))] + (let [inner []] + (each [_ tbl (ipairs tbls) &until (not (. tbl idx))] + (table.insert inner (. tbl idx))) + (table.insert result inner))))) + result)) + +(fn map [f ...] + (let [args [...] + tbls (zip (table.unpack args))] + (if (= 1 (count args)) + (icollect [_ v (pairs (first args))] + (apply f v)) + (accumulate [acc [] + _ t (ipairs tbls)] + (concat acc [(apply f (table.unpack t))]))))) + +(fn map-kv [f coll] + "Maps through an associative table, passing each k/v pair to f" + (icollect [k v (pairs coll)] + (f k v))) (fn merge [...] @@ -257,6 +286,7 @@ : last : logf : map + : map-kv : merge : noop : reduce diff --git a/windows.fnl b/windows.fnl index 54f6db3..331aa2e 100644 --- a/windows.fnl +++ b/windows.fnl @@ -3,9 +3,13 @@ : count : concat : contains? + : apply : map + : map-kv : for-each - : split} (require :lib.functional)) + : split + : logf + } (require :lib.functional)) (local {:global-filter global-filter} (require :lib.utils)) (local {:atom atom :deref deref @@ -436,7 +440,7 @@ (reset! screen-number-canvases [])) (fn monitor-item - [screen i] + [i screen] " Creates a menu item to move the frontMost window to the specified screen index Takes a hs.screen instance and an index integer @@ -471,7 +475,7 @@ Returns mutated modal menu table-map " (->> screens - (map monitor-item) + (map-kv monitor-item) (concat menu.items) (tset menu :items)) menu) From 4d2512e2117cb374fbdda1000faae35294d338c4 Mon Sep 17 00:00:00 2001 From: Ag Ibragimov Date: Thu, 22 Aug 2024 16:51:25 -0500 Subject: [PATCH 2/6] removes concat's dup Suggested-by: Grazfather https://github.com/agzam/spacehammer/pull/193/files#r1714459770 --- lib/functional.fnl | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/functional.fnl b/lib/functional.fnl index 55bd92f..73bc7d4 100644 --- a/lib/functional.fnl +++ b/lib/functional.fnl @@ -221,16 +221,6 @@ [] tbl)) -(fn concat - [...] - (reduce - (fn [cat tbl] - (each [_ v (ipairs tbl)] - (table.insert cat v)) - cat) - [] - [...])) - (fn some [f tbl] (let [filtered (filter f tbl)] From 9f9cf8b30fc41cd85930204d8d3ca482d5af4dbe Mon Sep 17 00:00:00 2001 From: Ag Ibragimov Date: Thu, 22 Aug 2024 16:56:22 -0500 Subject: [PATCH 3/6] don't unpack, apply does it already Suggested-by: Grazfather https://github.com/agzam/spacehammer/pull/193/files#r1714427181 --- lib/functional.fnl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/functional.fnl b/lib/functional.fnl index 73bc7d4..4f46b5f 100644 --- a/lib/functional.fnl +++ b/lib/functional.fnl @@ -177,12 +177,11 @@ result []] (if (= 1 (length tbls)) (table.insert result (. tbls 1)) - (let [] - (for [idx 1 (length (. tbls 1))] - (let [inner []] - (each [_ tbl (ipairs tbls) &until (not (. tbl idx))] - (table.insert inner (. tbl idx))) - (table.insert result inner))))) + (for [idx 1 (length (. tbls 1))] + (let [inner []] + (each [_ tbl (ipairs tbls) &until (not (. tbl idx))] + (table.insert inner (. tbl idx))) + (table.insert result inner)))) result)) (fn map [f ...] @@ -193,7 +192,7 @@ (apply f v)) (accumulate [acc [] _ t (ipairs tbls)] - (concat acc [(apply f (table.unpack t))]))))) + (concat acc [(apply f t)]))))) (fn map-kv [f coll] "Maps through an associative table, passing each k/v pair to f" From 09ef9959643a83e12ec27bd8c8af098e07c3a595 Mon Sep 17 00:00:00 2001 From: Ag Ibragimov Date: Thu, 22 Aug 2024 17:19:25 -0500 Subject: [PATCH 4/6] adds basic (map) tests --- test/functional-test.fnl | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/test/functional-test.fnl b/test/functional-test.fnl index 2ae37de..d298e1b 100644 --- a/test/functional-test.fnl +++ b/test/functional-test.fnl @@ -47,4 +47,28 @@ (fn [] (is.eq? (f.reduce #(.. $1 $2) "" [5 4 3 2 1]) "54321" "reduce did not concat list into string") (is.eq? (f.reduce #(if (> $1 $3) $1 $3) 0 [1 3 5 2 0]) 5 "reduce did not find max"))) - )) + + (it "(map) traverses a table" + (fn [] + (is.eq? + (fennel.view (f.map (fn [x] x) [:a :b :c])) + (fennel.view [:a :b :c]) + "same table"))) + + (it "(map) traverses a table with a transform" + (fn [] + (is.eq? + (fennel.view (f.map (fn [x] (string.upper x)) [:a :b :c])) + (fennel.view [:A :B :C]) + "capitalized"))) + + (it "(map) traverses multiple tables" + (fn [] + (is.eq? + (fennel.view + (f.map + (fn [a b] + (f.concat b a)) [:a :b :c] [1 2 3])) + (fennel.view + [[:a 1] [:b 2] [:c 3]]) + "data from both tables appear"))))) From c014146156d19696265f3ee35907046a1c618b35 Mon Sep 17 00:00:00 2001 From: Ag Ibragimov Date: Thu, 22 Aug 2024 18:55:25 -0500 Subject: [PATCH 5/6] export apply --- lib/functional.fnl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/functional.fnl b/lib/functional.fnl index 4f46b5f..505c858 100644 --- a/lib/functional.fnl +++ b/lib/functional.fnl @@ -255,7 +255,8 @@ ;; Exports ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -{: butlast +{: apply + : butlast : call-when : compose : concat @@ -281,7 +282,7 @@ : reduce : seq : seq? - : some : slice + : some : split : tap} From 2a753cdb6b96db549304d8778f7a128e7ba408b7 Mon Sep 17 00:00:00 2001 From: Ag Ibragimov Date: Thu, 29 Aug 2024 12:52:23 -0500 Subject: [PATCH 6/6] eq? -> seq-eq? I only couldn't convert the last clause --- test/functional-test.fnl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/functional-test.fnl b/test/functional-test.fnl index d298e1b..fdb8290 100644 --- a/test/functional-test.fnl +++ b/test/functional-test.fnl @@ -50,25 +50,25 @@ (it "(map) traverses a table" (fn [] - (is.eq? - (fennel.view (f.map (fn [x] x) [:a :b :c])) - (fennel.view [:a :b :c]) + (is.seq-eq? + (f.map (fn [x] x) [:a :b :c]) + [:a :b :c] "same table"))) (it "(map) traverses a table with a transform" (fn [] - (is.eq? - (fennel.view (f.map (fn [x] (string.upper x)) [:a :b :c])) - (fennel.view [:A :B :C]) + (is.seq-eq? + (f.map (fn [x] (string.upper x)) [:a :b :c]) + [:A :B :C] "capitalized"))) (it "(map) traverses multiple tables" (fn [] - (is.eq? - (fennel.view - (f.map - (fn [a b] - (f.concat b a)) [:a :b :c] [1 2 3])) - (fennel.view - [[:a 1] [:b 2] [:c 3]]) - "data from both tables appear"))))) + (is.eq? + (fennel.view + (f.map + (fn [a b] + (f.concat b a)) [:a :b :c] [1 2 3])) + (fennel.view + [[:a 1] [:b 2] [:c 3]]) + "data from both tables appear")))))