From 8b6a45e43a432e6a523ca3f8b3f9067d40f6a7c4 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 6 Mar 2017 08:34:40 -0800 Subject: [PATCH] Remove `car` and `cdr` in favor of `first` and `rest` (#1241) * Remove uses of `car` and `cdr` in /hy * Remove uses of `car` and `cdr` in quote tests * Remove `car` and `cdr` in favor of `first` and `rest` I beefed up the documentation and tests for `first` and `rest` while I was at it. I defined `car` and `cdr` in native_tests.cons so the tests read a bit more naturally. --- docs/language/api.rst | 32 ++++++++++++++++++++++++-------- docs/language/core.rst | 4 ++-- hy/contrib/multi.hy | 12 ++++++------ hy/core/macros.hy | 14 ++------------ hy/extra/anaphoric.hy | 8 ++++---- tests/native_tests/cons.hy | 4 ++++ tests/native_tests/language.hy | 11 +++++++---- tests/native_tests/quote.hy | 8 ++++---- 8 files changed, 53 insertions(+), 40 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index a538f43..c4fa9d3 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -850,16 +850,26 @@ eval-when-compile ----------------- -first / car ------------ +first +----- -``first`` and ``car`` are macros for accessing the first element of a collection: +``first`` is a function for accessing the first element of a collection. .. code-block:: clj => (first (range 10)) 0 +It is implemented as ``(next (iter coll) None)``, so it works with any +iterable, and if given an empty iterable, it will return ``None`` instead of +raising an exception. + +.. code-block:: clj + + => (first (repeat 10)) + 10 + => (first []) + None for --- @@ -1402,17 +1412,23 @@ periods in them. In fact, ``mymodule`` and ``M`` aren't defined by these to do introspection of the current module's set of defined macros, which isn't really supported anyway. -rest / cdr ----------- +rest +---- -``rest`` and ``cdr`` return the collection passed as an argument without the -first element: +``rest`` takes the given collection and returns an iterable of all but the +first element. .. code-block:: clj - => (rest (range 10)) + => (list (rest (range 10))) [1, 2, 3, 4, 5, 6, 7, 8, 9] +Given an empty collection, it returns an empty iterable. + +.. code-block:: clj + + => (list (rest [])) + [] set-comp -------- diff --git a/docs/language/core.rst b/docs/language/core.rst index 652868f..b4cc8b0 100644 --- a/docs/language/core.rst +++ b/docs/language/core.rst @@ -111,10 +111,10 @@ Returns a fresh :ref:`cons cell ` with car *a* and cdr *b*. => (setv a (cons 'hd 'tl)) - => (= 'hd (car a)) + => (= 'hd (get a 0)) True - => (= 'tl (cdr a)) + => (= 'tl (cut a 1)) True diff --git a/hy/contrib/multi.hy b/hy/contrib/multi.hy index 62dbf14..e97617f 100644 --- a/hy/contrib/multi.hy +++ b/hy/contrib/multi.hy @@ -87,6 +87,9 @@ (with-decorator (method-decorator ~name) (defn ~name ~params ~@body)))) +(defn head-tail [l] + (, (get l 0) (cut l 1))) + (defmacro defn [name &rest bodies] (def arity-overloaded? (fn [bodies] (if (isinstance (first bodies) HyString) @@ -97,17 +100,14 @@ (do (def comment (HyString)) (if (= (type (first bodies)) HyString) - (do (def comment (car bodies)) - (def bodies (cdr bodies)))) + (def [comment bodies] (head-tail bodies))) (def ret `(do)) (.append ret '(import [hy.contrib.multi [MultiDispatch]])) (for [body bodies] - (def let-binds (car body)) - (def body (cdr body)) + (def [let-binds body] (head-tail body)) (.append ret `(with-decorator MultiDispatch (defn ~name ~let-binds ~comment ~@body)))) ret) (do - (setv lambda-list (first bodies)) - (setv body (rest bodies)) + (setv [lambda-list body] (head-tail bodies)) `(setv ~name (fn ~lambda-list ~@body))))) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 6d32c42..3177db8 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -59,16 +59,6 @@ `(do ~@body))) -(defmacro car [thing] - "Get the first element of a list/cons" - `(get ~thing 0)) - - -(defmacro cdr [thing] - "Get all the elements of a thing, except the first" - `(cut ~thing 1)) - - (defmacro cond [&rest branches] "shorthand for nested ifs: (cond [foo bar] [baz quux]) -> @@ -87,8 +77,8 @@ (macro-error branch "cond branches need to be a list")) (if (< (len branch) 2) (macro-error branch "cond branches need at least two items: a test and one or more code branches")) - (setv test (car branch)) - (setv thebranch (cdr branch)) + (setv test (first branch)) + (setv thebranch (cut branch 1)) `(if ~test (do ~@thebranch))) (setv root (check-branch branch)) diff --git a/hy/extra/anaphoric.hy b/hy/extra/anaphoric.hy index 2bbfc9c..20c77fc 100644 --- a/hy/extra/anaphoric.hy +++ b/hy/extra/anaphoric.hy @@ -114,8 +114,8 @@ (defmacro ap-reduce [form lst &optional [initial-value None]] "Anaphoric form of reduce, `acc' and `it' can be used for a form" `(do - (setv acc ~(if (none? initial-value) `(car ~lst) initial-value)) - (ap-each ~(if (none? initial-value) `(cdr ~lst) lst) + (setv acc ~(if (none? initial-value) `(get ~lst 0) initial-value)) + (ap-each ~(if (none? initial-value) `(cut ~lst 1) lst) (setv acc ~form)) acc)) @@ -142,11 +142,11 @@ (str i))) [i (range 1 ;; find the maximum xi - (inc (max (+ (list-comp (int (cdr a)) + (inc (max (+ (list-comp (int (cut a 1)) [a flatbody] (and (symbol? a) (.startswith a 'x) - (.isdigit (cdr a)))) + (.isdigit (cut a 1)))) [0]))))]) ;; generate the &rest parameter only if 'xi is present in body ~@(if (in 'xi flatbody) diff --git a/tests/native_tests/cons.hy b/tests/native_tests/cons.hy index 81e9eca..f5a1e6a 100644 --- a/tests/native_tests/cons.hy +++ b/tests/native_tests/cons.hy @@ -1,3 +1,7 @@ +(defmacro car [x] `(get ~x 0)) +(defmacro cdr [x] `(cut ~x 1)) + + (defn test-cons-mutability [] "Test the mutability of conses" (setv tree (cons (cons 1 2) (cons 2 3))) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index d4ac7e5..0b29efd 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -681,10 +681,11 @@ (defn test-first [] - "NATIVE: test firsty things" + "NATIVE: test first" (assert (= (first [1 2 3 4 5]) 1)) - (assert (is (first []) None)) - (assert (= (car [1 2 3 4 5]) 1))) + (assert (= (first (range 10)) 0)) + (assert (= (first (repeat 10)) 10)) + (assert (is (first []) None))) (defn test-cut [] @@ -710,7 +711,9 @@ (defn test-rest [] "NATIVE: test rest" - (assert (= (list (rest [1 2 3 4 5])) [2 3 4 5]))) + (assert (= (list (rest [1 2 3 4 5])) [2 3 4 5])) + (assert (= (list (take 3 (rest (iterate inc 8)))) [9 10 11])) + (assert (= (list (rest [])) []))) (defn test-importas [] diff --git a/tests/native_tests/quote.hy b/tests/native_tests/quote.hy index 750aaf3..617de5d 100644 --- a/tests/native_tests/quote.hy +++ b/tests/native_tests/quote.hy @@ -11,8 +11,8 @@ (defn test-quoted-hoistable [] "NATIVE: check whether quote works on hoisted things" (setv f (quote (if True True True))) - (assert (= (car f) (quote if))) - (assert (= (cdr f) (quote (True True True))))) + (assert (= (get f 0) (quote if))) + (assert (= (cut f 1) (quote (True True True))))) (defn test-quoted-macroexpand [] @@ -20,8 +20,8 @@ (setv q1 (quote (-> a b c))) (setv q2 (quasiquote (-> a b c))) (assert (= q1 q2)) - (assert (= (car q1) (quote ->))) - (assert (= (cdr q1) (quote (a b c))))) + (assert (= (get q1 0) (quote ->))) + (assert (= (cut q1 1) (quote (a b c))))) (defn test-quote-dicts []