{"id":15010694,"url":"https://github.com/killme2008/defun","last_synced_at":"2025-04-07T23:11:37.256Z","repository":{"id":20731037,"uuid":"24015236","full_name":"killme2008/defun","owner":"killme2008","description":"A macro to define clojure functions with parameter pattern matching just like erlang or elixir.","archived":false,"fork":false,"pushed_at":"2023-10-20T16:42:02.000Z","size":48,"stargazers_count":477,"open_issues_count":1,"forks_count":20,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-03-31T22:22:21.486Z","etag":null,"topics":["clojure","clojure-functions","defun","pattern-matching"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/killme2008.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2014-09-14T06:29:15.000Z","updated_at":"2024-12-21T00:45:48.000Z","dependencies_parsed_at":"2024-01-08T18:03:38.407Z","dependency_job_id":"e10ff531-f7c9-40a2-af7a-3ec703166228","html_url":"https://github.com/killme2008/defun","commit_stats":{"total_commits":40,"total_committers":7,"mean_commits":5.714285714285714,"dds":0.35,"last_synced_commit":"5f6ac2e70c0ff42ac7ae41d924b993472cf89ed3"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/killme2008%2Fdefun","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/killme2008%2Fdefun/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/killme2008%2Fdefun/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/killme2008%2Fdefun/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/killme2008","download_url":"https://codeload.github.com/killme2008/defun/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247744335,"owners_count":20988783,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","clojure-functions","defun","pattern-matching"],"created_at":"2024-09-24T19:35:23.785Z","updated_at":"2025-04-07T23:11:37.225Z","avatar_url":"https://github.com/killme2008.png","language":"Clojure","funding_links":[],"categories":["Pattern Matching"],"sub_categories":[],"readme":"[![Clojure CI](https://github.com/killme2008/defun/actions/workflows/clojure.yml/badge.svg?branch=master)](https://github.com/killme2008/defun/actions/workflows/clojure.yml)\n\n# defun\n\nA macro to define clojure functions with pattern matching just as erlang or elixir. It supports both clojure and clojurescript.\n\n## Usage\n\nDependency in leiningen:\n\n``` clj\n    [defun \"0.4.0\"]\n```\n\n### Basic usage\n\nRequire `defun.core` in clojure:\n\n```clj\n(require '[defun.core :refer [defun]])\n```\n\nOr `refer-macros` in clojurescript:\n\n```cljs\n(ns cljs-test\n  (:require  [defun.core :refer-macros [defun]])\n(enable-console-print!)\n```\n\nTry to define function just like `defn`:\n\n``` clj\n(defun hello\n   \"hello world\"\n   [name] (str \"hello,\" name))\n(hello \"defun\")\n;; \"hello,defun\"\n```\n\nSupports variadic arguments, doc, metadata etc. as `defun` too. No surprises?\n\nThe fun thing is coming, let's say hi to people:\n\n``` clj\n(defun say-hi\n  ([:dennis] \"Hi,good morning, dennis.\")\n  ([:catty] \"Hi, catty, what time is it?\")\n  ([:green] \"Hi,green, what a good day!\")\n  ([other] (str \"Say hi to \" other)))\n```\n\nThen calling `say-hi` with different names:\n\n``` clj\n(say-hi :dennis)\n;;  \"Hi,good morning, dennis.\"\n(say-hi :catty)\n;;  \"Hi, catty, what time is it?\"\n(say-hi :green)\n;;  \"Hi,green, what a good day!\"\n(say-hi \"someone\")\n;;  \"Say hi to someone\"\n```\n\nWe define functions just like Erlang's function with parameters pattern match (thanks to [core.match](https://github.com/clojure/core.match)), we don't need `if,cond,case` any more, that's cool!\n\n### Recursion\n\nLet's move on, what about define a recursive function? That's easy too:\n\n``` clj\n(defun count-down\n  ([0] (println \"Reach zero!\"))\n  ([n] (println n)\n     (recur (dec n))))\n```\n\nInvoke it:\n\n``` clj\n(count-down 5)\n;;5\n;;4\n;;3\n;;2\n;;1\n;;Reach zero!\nnil\n```\n\nAn accumulator from zero to number `n`:\n\n``` clj\n(defun accum\n  ([0 ret] ret)\n  ([n ret] (recur (dec n) (+ n ret)))\n  ([n] (recur n 0)))\n\n(accum 100)\n;;5050\n```\n\nA fibonacci function:\n\n``` clj\n(defun fib\n    ([0] 0)\n    ([1] 1)\n    ([n] (+ (fib (- n 1)) (fib (- n 2)))))\n```\n\nOutput:\n\n``` clj\n(fib 10)\n;; 55\n```\n\nOf course it's not tail recursive, but it's really cool, isn't it?\n\n### Guards\n\nAdded a guard function to parameters:\n\n``` clj\n(defun funny\n  ([(N :guard #(= 42 %))] true)\n  ([_] false))\n\n(funny 42)\n;;  true\n(funny 43)\n;; false\n```\n\nAnother function to detect if longitude and latitude values are both valid:\n\n``` clj\n(defun valid-geopoint?\n    ([(_ :guard #(and (\u003e % -180) (\u003c % 180)))\n      (_ :guard #(and (\u003e % -90) (\u003c % 90)))] true)\n    ([_ _] false))\n\n(valid-geopoint? 30 30)\n;; true\n(valid-geopoint? -181 30)\n;; false\n```\n\n### Private defun\n\nOf course, you can use `defun-` to define a function that is private just as `defn-`\n\n### More Patterns\n\nIn fact ,the above `say-hi` function will be expanded to be:\n\n``` clj\n(defn\n say-hi\n {:arglists '([\u0026 args])}\n [\u0026 args#]\n (clojure.core.match/match\n  [(vec args#)]\n  [[:dennis]]\n  (do \"Hi,good morning, dennis.\")\n  [[:catty]]\n  (do \"Hi, catty, what time is it?\")\n  [[:green]]\n  (do \"Hi,green, what a good day!\")\n  [[other]]\n  (do (str \"Say hi to \" other))))\n```\n\nThe argument vector is in fact a pattern in core.match, so we can use all patterns that supported by [core.match](https://github.com/clojure/core.match/wiki/Basic-usage).\n\nFor example, matching literals\n\n``` clj\n(defun test1\n    ([true false] 1)\n    ([true true] 2)\n    ([false true] 3)\n    ([false false] 4))\n\n(test1 true true)\n;; 2\n(test1 false false)\n;; 4\n```\n\nMatching sequence:\n\n``` clj\n(defun test2\n    ([([1] :seq)] :a0)\n    ([([1 2] :seq)] :a1)\n    ([([1 2 nil nil nil] :seq)] :a2))\n\n(test2 [1 2 nil nil nil])\n;; a2\n```\n\nMatching vector:\n\n``` clj\n(defun test3\n    ([[_ _ 2]] :a0)\n    ([[1 1 3]] :a1)\n    ([[1 2 3]] :a2))\n\n(test3 [1 2 3])\n;; :a2\n```\n\nRest Pattern, Map Pattern, Or Pattern etc.\n\nI don't want to copy the [core.match's wiki](https://github.com/clojure/core.match/wiki/Basic-usage),please visit it by yourself.\n\n### fun and letfun\n\n``` clojure\n((fun\n    ([[_ _ 2]] :a0)\n    ([[1 1 3]] :a1)\n    ([[1 2 3]] :a2))\n  [1 2 3])\n;; :a2\n\n(letfun [(test3 ([[_ _ 2]] :a0)\n                    ([[1 1 3]] :a1)\n                    ([[1 2 3]] :a2))]\n  (test3 [1 2 3]))\n;; :a2\n```\n\n\n\n## Criterium benchmarking\n\nUses the above function `accum` compared with a normal clojure function:\n\n``` clj\n(require '[criterium.core :refer [bench]])\n\n(defn accum-defn\n    ([n] (accum-defn 0 n))\n    ([ret n] (if (= n 0) ret (recur (+ n ret) (dec n)))))\n\n(defun accum-defun\n  ([0 ret] ret)\n  ([n ret] (recur (dec n) (+ n ret)))\n  ([n] (recur n 0)))\n\n(bench (accum-defn 10000))\n;;Evaluation count : 106740 in 60 samples of 1779 calls.\n;;             Execution time mean : 578.777537 µs\n;;    Execution time std-deviation : 23.354350 µs\n;;   Execution time lower quantile : 552.627735 µs ( 2.5%)\n;;   Execution time upper quantile : 637.001868 µs (97.5%)\n;;                   Overhead used : 17.111650 ns\n\n(bench (accum-defun 10000))\n;;Evaluation count : 54660 in 60 samples of 911 calls.\n;;             Execution time mean : 1.115643 ms\n;;    Execution time std-deviation : 32.916487 µs\n;;   Execution time lower quantile : 1.078117 ms ( 2.5%)\n;;   Execution time upper quantile : 1.180711 ms (97.5%)\n;;                   Overhead used : 17.111650 ns\n```\n\naccum-defn is faster than accum-defun. Pattern matching does have a tradeoff.\n\n## Contributors\n\nThanks .\n\n- [kgann](https://github.com/kgann)\n- [danielcompton](https://github.com/danielcompton)\n- [Sander Dijkhuis](https://github.com/sander)\n- [sskorokhodov](https://github.com/sskorokhodov)\n\n## License\n\nCopyright © 2023 [Dennis Zhuang](mailto:killme2008@gmail.com)\n\nDistributed under the Eclipse Public License either version 1.0 or (at\n\nyour option) any later version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkillme2008%2Fdefun","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkillme2008%2Fdefun","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkillme2008%2Fdefun/lists"}