Last active
September 28, 2015 16:20
-
-
Save nathanmarz/8b8efaa53c5c3a66d5bb to your computer and use it in GitHub Desktop.
functional navigational programming blog post
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn pay-fee [world] | |
(transfer world | |
[:people ALL :money] | |
[:bank :funds] | |
1)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn bank-give-dollar [world] | |
(transfer world | |
[:bank :funds] | |
[:people ALL :money] | |
1)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn user [name] | |
[:people | |
ALL | |
#(= (:name %) name)]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn transfer-users [world from to amt] | |
(transfer world | |
[(user from) :money] | |
[(user to) :money] | |
amt)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn user->bank [world from amt] | |
(transfer world | |
[(user from) :money] | |
[:bank :funds] | |
amt)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn bank-loyal-bonus [world] | |
(transfer world | |
[:bank :funds] | |
[:people (srange 0 3) ALL :money] | |
5000)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(def DATA {:a {:b {:c 1}}}) | |
(def compiled-path (comp-paths :a :b :c)) | |
(benchmark 1000000 #(get-in DATA [:a :b :c])) | |
;; => "Elapsed time: 77.018 msecs" | |
(benchmark 1000000 #(select [:a :b :c] DATA)) | |
;; => "Elapsed time: 4143.343 msecs" | |
(benchmark 1000000 #(select compiled-path DATA)) | |
;; => "Elapsed time: 63.183 msecs" | |
(benchmark 1000000 #(compiled-select compiled-path DATA)) | |
;; => "Elapsed time: 51.964 msecs" | |
(benchmark 1000000 #(-> DATA :a :b :c vector)) | |
;; => "Elapsed time: 34.235 msecs" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(benchmark 1000000 #(update-in DATA [:a :b :c] inc)) | |
;; => "Elapsed time: 1037.94 msecs" | |
(benchmark 1000000 #(transform [:a :b :c] inc DATA)) | |
;; => "Elapsed time: 4305.429 msecs" | |
(benchmark 1000000 #(transform compiled-path inc DATA)) | |
;; => "Elapsed time: 184.593 msecs" | |
(benchmark 1000000 #(compiled-transform compiled-path inc DATA)) | |
;; => "Elapsed time: 169.841 msecs" | |
(defn manual-transform [data] | |
(update data | |
:a | |
(fn [d1] | |
(update d1 | |
:b | |
(fn [d2] | |
(update d2 :c inc)))))) | |
(benchmark 1000000 #(manual-transform DATA)) | |
;; => "Elapsed time: 161.945 msecs" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(def compiled-path (comp-paths ALL :a even?)) | |
(transform compiled-path | |
inc | |
[{:a 2 :b 3} {:a 1} {:a 4}]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(transform [(srange 4 11) (filterer even?)] | |
reverse | |
[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn reverse-matching-in-range [aseq start end predicate] | |
(transform [(srange start end) (filterer predicate)] | |
reverse | |
aseq)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(let [compiled-path (comp-paths srange (filterer pred))] | |
(defn reverse-matching-in-range [aseq start end predicate] | |
(compiled-transform (compiled-path start end predicate) | |
reverse | |
aseq))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(def user | |
(comp-paths :people | |
ALL | |
(paramsfn [name] | |
[elem] | |
(= name (:name elem))) | |
)) | |
(def user-money (comp-paths user :money)) | |
(def BANK-MONEY (comp-paths :bank :funds)) | |
(defn user->bank [world name amt] | |
(transfer world (user-money name) BANK-MONEY amt)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(transform [ALL :a even?] | |
inc | |
[{:a 2 :b 3} {:a 1} {:a 4}]) | |
;; => [{:a 3 :b 3} {:a 1} {:a 5}] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(transform [(filterer odd?) LAST] | |
inc | |
[2 1 3 6 9 4 8]) | |
;; => [2 1 3 6 10 4 8] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(transform [(srange 4 11) (filterer even?)] | |
reverse | |
[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]) | |
;; => [0 1 2 3 10 5 8 7 6 9 4 11 12 13 14 15] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(select [ALL ALL #(= 0 (mod % 3))] | |
[[1 2 3 4] [] [5 3 2 18] [2 4 6] [12]]) | |
;;=> [3 3 18 6 12] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(extend-type clojure.lang.Keyword | |
StructurePath | |
(select* [kw structure next-fn] | |
(next-fn (get structure kw))) | |
(transform* [kw structure next-fn] | |
(assoc structure kw (next-fn (get structure kw))) | |
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn user->bank [world name amt] | |
(let [;; First, find out how much money that user has | |
;; to determine whether or not this is a valid transfer | |
curr-funds (->> world | |
:people | |
(filter (fn [user] (= (:name user) name))) | |
first | |
:money | |
)] | |
(if (< curr-funds amt) | |
(throw (IllegalArgumentException. "Not enough funds!")) | |
;; If valid, then need to subtract the transfer amount from the | |
;; user and add the amount to the bank | |
(-> world | |
(update | |
:people | |
(fn [user-list] | |
;; Important to use mapv to maintain the type of the | |
;; sequence containing the list of users. This code | |
;; modifies the user matching the name and keeps | |
;; every other user in the sequence the same. | |
(mapv (fn [user] | |
;; Notice how nested this code is that manipulates the users | |
(if (= (:name user) name) | |
(update user :money #(+ % amt)) | |
;; If a user doesn't match the name during the scan, | |
;; don't modify them | |
user | |
)) | |
user-list))) | |
(update-in | |
[:bank :funds] | |
#(- % amt)) | |
)))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defprotocol StructurePath | |
(select* [this structure next-fn]) | |
(transform* [this structure next-fn]) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn transfer | |
"Note that this function works on *any* world structure. This handles | |
arbitrary many to many transfers of a fixed amount without overdrawing anyone" | |
[world from-path to-path amt] | |
(let [;; Get the sequence of funds for all entities making a transfer | |
givers (select from-path world) | |
;; Get the sequence of funds for all entities receiving a transfer | |
receivers (select to-path world) | |
;; Compute total amount each receiver will be credited | |
total-receive (* amt (count givers)) | |
;; Compute total amount each transferrer will be deducted | |
total-give (* amt (count receivers))] | |
;; Make sure every transferrer has sufficient funds | |
(if (every? #(>= % total-give) givers) | |
(->> world | |
;; Deduct from transferrers | |
(transform from-path #(- % total-give)) | |
;; Credit the receivers | |
(transform to-path #(+ % total-receive)) | |
) | |
(throw (IllegalArgumentException. "Not enough funds!")) | |
))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(def world | |
{:people [{:money 129827 :name "Alice Brown"} | |
{:money 100 :name "John Smith"} | |
{:money 6821212339 :name "Donald Trump"} | |
{:money 2870 :name "Charlie Johnson"} | |
{:money 8273821 :name "Charlie Rose"} | |
] | |
:bank {:funds 4782328748273}} | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment