mcpField
M
monoids

monoids

github.com/bsene开发745 次安装1 周前更新
代码仓库

关于 monoids

monoids 是一项将幺半群概念应用于 Clojure 和 ClojureScript 代码的技能,帮助识别合并与折叠中的结合运算及单位元,也涵盖可并行化的情况。

主要功能

  1. 01识别结合运算与单位元
  2. 02将 Clojure 核心函数用作幺半群
  3. 03无需特殊分支处理空集合
  4. 04利用结合律进行并行聚合
  5. 05判断运算是否不满足幺半群条件

适用场景

  1. 01用可复用运算替代临时合并逻辑
  2. 02让折叠稳健处理空集合
  3. 03并行执行满足结合律的聚合
  4. 04建模组合后的权限、计数器或配置

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/bsene/skills --skill monoids

安装后在 Claude Code 中输入/monoids确认已加载。

详细介绍

幺半群

幺半群是一种极简结构:一组值 + 一个满足结合律的二元运算(combine)+ 一个单位元(empty)。除此之外别无其他。在业务代码中识别出这种结构,就能用一套通用且经过一次性测试的 API,替代临时编写的合并逻辑——后者是边界情况中常见的 bug 来源。

本技能涵盖 Clojure/ClojureScript。两个语言中的概念相同,只有惯用写法不同。TypeScript 惯用写法(接口 Monoid<T>、折叠、业务幺半群)现已移至 typescript 技能 → ../typescript/composition/SKILL.md(Monoids 章节);请将 TS 问题引导至该参考资料。

参考资料:Les monoïdes : une abstraction omniprésente (evryg)

何时应用此技能

要留意的信号不是“幺半群”这个词,而是请求或代码中出现以下任一模式:

  • 为特定类型手动编写的 merge(a, b)、combine(a, b) 或 add(a, b) 函数
  • 对列表执行 reduce/fold,并使用任意初始值
  • 聚合计算前有 if (list.length === 0) throw / return nil
  • 希望并行化聚合计算(Map-Reduce、Kafka 分区聚合等)
  • 自然可以“相加”的业务建模:权限、计数器、日志、配置

如果出现这些模式之一,请主动提出幺半群结构,而不是等用户提起。


Clojure / ClojureScript

在 Clojure 中,这种抽象通常是隐式的:相比将 Monoid 接口具体化,语言更鼓励对已有结合律且具有自然单位元的函数使用 reduce。许多 core 函数(+、*、str、into、merge-with、concat、clojure.set/union)本身就是幺半群——它们的零参数元数直接编码了单位元。

接口(如需显式具体化)
(defrecord Monoid [empty combine])

(defn fold [{:keys [empty combine]} items]
  (reduce combine empty items))

实际使用时,优先采用下面的 core 函数,而不是将其具体化——这更符合惯用写法,而且可以直接获得单位元。

基本幺半群
;; Somme — (+) => 0, le neutre est intégré à l'arité 0
(reduce + [1 2 3])              ;; => 6

;; Concaténation de string
(apply str ["a" "b" "c"])       ;; => "abc"

;; Concaténation de collections
(reduce into [] [[1 2] [3 4]])  ;; => [1 2 3 4]

;; Booléens
(every? true? [true true false])  ;; and — neutre true
(some true? [false false true])   ;; or — neutre false

;; Composition de fonctions — (comp) => identity
(comp inc #(* % 2))
结合律 → 并行化
(defn parallel-fold [f init coll chunks]
  (->> (partition-all (/ (count coll) chunks) coll)
       (pmap #(reduce f init %))
       (reduce f init)))

(parallel-fold + 0 (range 1000000) 4)

pmap 会在可用核心上分发;f 满足结合律,因此分块分组不会影响结果——这与分布式幺半群折叠的逻辑相同。

业务建模
;; Panier — merge-with est LE combinateur idiomatique pour les maps
(merge-with + {:apple 2 :banana 1} {:apple 1 :cherry 3})
;; => {:apple 3, :banana 1, :cherry 3}

;; Permissions — clojure.set/union est associatif, #{} est le neutre
(require '[clojure.set :as set])
(apply set/union [#{:read} #{:write} #{:read :admin}])
;; => #{:read :write :admin}

;; Event log — simple concaténation, [] est le neutre
(apply concat [[:e1 :e2] [:e3] [:e4 :e5]])
;; => (:e1 :e2 :e3 :e4 :e5)
单位元 → 健壮性

可变参数函数原生编码了单位元——“集合为空”的情况永远不会导致失败,无需特殊分支:

(reduce + [])       ;; => 0, pas d'exception
(apply str [])       ;; => ""
(merge-with + {})    ;; => {}

这里的单位元通常已经是函数零参数元数的默认行为——这表明该函数的设计考虑了代数性质。

可组合性
;; 1. "Produit de monoïdes" — juxt applique plusieurs fonctions et combine leurs résultats
(defn count-and-sum [coll]
  ((juxt count (partial reduce +)) coll))
(count-and-sum [10 20 30])  ;; => [3 60]

;; 2. Fonctions X -> M — combiner ponctuellement deux fonctions vers un monoïde
(defn combine-fns [f g combine]
  (fn [x] (combine (f x) (g x))))

;; 3. Map<K, monoïde> — merge-with généralise à n'importe quel combine
(merge-with into
            {:errors [] :warnings [:w1]}
            {:errors [:e1] :warnings [:w2]})
;; => {:errors [:e1], :warnings [:w1 :w2]}

;; Compteurs d'événements par type — pattern fréquent en event-driven
(defn count-events [events]
  (reduce (fn [acc e] (merge-with + acc {(:type e) 1}))
          {}
          events))
Clojure 特有的陷阱

不带 init 的 (reduce f coll) 即使没有显式单位元也能运行——第一个元素会作为初始累加器。这很方便,但可能掩盖单位元实际上并不存在:如果该领域没有自然的单位元,那么它就是半群,而不是幺半群;此时并行化或处理空集合的风险会比表面看起来更高。


注意事项

  • 如果运算实际上不满足结合律(例如减法、除法),不要强行套用这种抽象——先验证该定律再提出,快速举一个反例就够了。
  • 如果该领域不存在自然的单位元,它可能不是幺半群,而是半群(满足结合律但没有单位元)——应指出这一点,而不是凭空编造一个单位元。
  • 保持务实:目标是用通用 API 替代有 bug 的临时代码,而不是为了显得专业就强行使用函数式术语。在 Clojure 中,如果 merge-with/reduce 配合 core 函数就足够了,就不要多此一举地建议具体化 Monoid。

基准测试

场景:.benchmarks/scenarios/monoids-001-parallel-merge.md · 运行:2026-08-31 · 日志:.benchmarks/runs/2026-08-31/monoids-001-parallel-merge.json

模型未启用已启用变化
claude-opus-4-8100%100%+0%
claude-sonnet-4-683%100%+17%
claude-haiku-4-5100%100%+0%

有条件通过(运行于 2026-08-31)。变化幅度为零是预期结果:法语正文(# Monoïdes)不会对英文提示词触发(审计警告)。Sonnet 提升 17 个百分点(83→100);opus/haiku 无变化。门槛依据 .agents/skills/skill-optimizer/rules/release-gates.md。

评论

支持 Markdown