Skip to content

Instantly share code, notes, and snippets.

@lagenorhynque
Last active April 11, 2025 01:25
Show Gist options
  • Save lagenorhynque/bec8e4a2e856656832605f08dfa8e6d1 to your computer and use it in GitHub Desktop.
Save lagenorhynque/bec8e4a2e856656832605f08dfa8e6d1 to your computer and use it in GitHub Desktop.
『改訂新版 良いコード/悪いコードで学ぶ設計入門』(ミノ駆動本) 3章のサンプルコードを関数型言語Clojureで書いてみる
(ns money
(:refer-clojure :exclude [+])
(:require
[clojure.spec.alpha :as s])
(:import
(java.util
Currency)))
(s/def ::amount nat-int?)
(s/def ::currency #(instance? Currency %))
(s/def ::money
(s/keys :req-un [::amount
::currency]))
(defrecord Money
[amount
currency])
(s/fdef make-money
:args (s/cat :amount ::amount
:currency ::currency)
:ret ::money)
(defn make-money
[amount currency]
(map->Money
{:amount amount
:currency currency}))
(s/fdef +
:args (s/and (s/+ ::money)
#(apply = (map :currency %)))
:ret ::money)
(defn +
([m] m)
([m1 m2]
(update m1
:amount
clojure.core/+ (:amount m2)))
([m1 m2 & more]
(reduce + (+ m1 m2) more)))
(comment
(require '[clojure.repl.deps :refer [add-lib]])
(add-lib 'orchestra)
(require '[orchestra.spec.test :as stest])
(stest/instrument)
;;; 利用例
(make-money 100 (Currency/getInstance "USD"))
;; => {:amount 100, :currency #object[java.util.Currency 0x74f08418 "USD"]}
;; 負の数量なので生成できない
(make-money -100 (Currency/getInstance "USD"))
;; => Execution error - invalid arguments to money/make-money at (test.cljc:30).
;; -100 - failed: nat-int? at: [:amount] spec: :money/amount
(let [usd (Currency/getInstance "USD")
m1 (make-money 100 usd)
m2 (make-money 200 usd)
m3 (make-money 300 usd)]
(+ m1 m2 m3))
;; => {:amount 600, :currency #object[java.util.Currency 0x74f08418 "USD"]}
;; 異なる通貨の和は計算できない
(let [usd (Currency/getInstance "USD")
jpy (Currency/getInstance "JPY")
m1 (make-money 100 usd)
m2 (make-money 200 usd)
m3 (make-money 300 jpy)]
(+ m1 m2 m3))
;; => Execution error - invalid arguments to money/+ at (test.cljc:30).
;; [#money.Money{:amount 100, :currency #object[java.util.Currency 0x74f08418 "USD"]} #money.Money{:amount 200, :currency #object[java.util.Currency 0x74f08418 "USD"]} #money.Money{:amount 300, :currency #object[java.util.Currency 0x5a87e137 "JPY"]}] - failed: (apply = (map :currency %))
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment