mirror of
https://github.com/carp-lang/Carp.git
synced 2024-09-20 01:47:18 +03:00
commit
4ceb9b513d
136
core/Macros.carp
136
core/Macros.carp
@ -119,6 +119,142 @@
|
||||
(defndynamic collect-into [xs f]
|
||||
(list 'quote
|
||||
(collect-into-internal xs (f) f)))
|
||||
|
||||
(doc empty?
|
||||
"Returns true if the provided data literal is empty, false otherwise.")
|
||||
(defndynamic empty? [xs]
|
||||
(= 0 (length xs)))
|
||||
|
||||
(doc flip
|
||||
"Flips the arguments of a function `f`.
|
||||
|
||||
For example,
|
||||
|
||||
```
|
||||
((flip Symbol.prefix) 'Bar 'Foo)
|
||||
=> ;; (Foo.Bar)
|
||||
```")
|
||||
(defndynamic flip [f]
|
||||
(fn [x y]
|
||||
(f y x)))
|
||||
|
||||
;; Dynamic.or already exists, but since it's a special form, it can't be passed
|
||||
;; to higher order functions like reduce. So, we define an alternative here.
|
||||
(defndynamic or-internal [x y]
|
||||
(if x true y))
|
||||
|
||||
(doc curry
|
||||
"Returns a curried function accepting a single argument, that applies f to x
|
||||
and then to the following argument.
|
||||
|
||||
For example,
|
||||
|
||||
```
|
||||
(map (curry Symbol.prefix 'Foo) '(bar baz))
|
||||
;; => (Foo.bar Foo.baz)
|
||||
```")
|
||||
(defndynamic curry [f x]
|
||||
(fn [y]
|
||||
(f x y)))
|
||||
|
||||
(doc reduce
|
||||
"Reduces or 'folds' a data literal, such as a list or array, into a single
|
||||
value through successive applications of `f`.")
|
||||
(defndynamic reduce [f x xs]
|
||||
(if (empty? xs)
|
||||
x
|
||||
(reduce f (f x (car xs)) (cdr xs))))
|
||||
|
||||
(doc apply
|
||||
"Applies the function `f` to the provided argument list, passing each value
|
||||
in the list as an argument to the function.")
|
||||
(defndynamic apply [f argument-list]
|
||||
;; The let clause here is a tad mysterious at first glance. When passed a
|
||||
;; standalone function name (i.e. not an application (f x), carp evaluates
|
||||
;; it into the function's defining form, e.g. foo becomes (defn foo [x] x),
|
||||
;; commands such as + become (command +) etc. ;; The binding here accounts
|
||||
;; for that case, allowing users to pass the function name to apply
|
||||
;; unquoted.
|
||||
;;
|
||||
;; This is necessary for parity across map-internal, zip, and apply.
|
||||
;; Since map calls its function directly, it takes it as is. Apply, on the
|
||||
;; other hand, would have to take the quoted form, since it builds a list
|
||||
;; that serves as the actual application.
|
||||
;;
|
||||
;; This is problematic for the user facing map function, since it makes
|
||||
;; calls to map or zip (which uses apply) as appropriate--unless we support
|
||||
;; the quoted function name argument in map-internal or the unquoted one in
|
||||
;; apply, we can't use zip and map-internal in map.
|
||||
(let [function-name (list (cadr f))]
|
||||
(append function-name argument-list)))
|
||||
|
||||
(hidden map-internal)
|
||||
(defndynamic map-internal [f xs acc]
|
||||
(if (empty? xs)
|
||||
acc
|
||||
(map-internal f (cdr xs) (cons-last (f (car xs)) acc))))
|
||||
|
||||
(hidden zip-internal)
|
||||
(defndynamic zip-internal [f forms acc]
|
||||
(if (reduce or-internal false (map-internal empty? forms (list)))
|
||||
acc
|
||||
(zip-internal
|
||||
f
|
||||
(map-internal cdr forms (list))
|
||||
(let [result (list (apply f (map-internal car forms (list))))]
|
||||
(append acc result)))))
|
||||
|
||||
(doc zip
|
||||
"Returns the *form* that results from applying a function `f` to each of
|
||||
the values supplied in `forms`.
|
||||
|
||||
If the members of a single form are exhuasted, the result of the
|
||||
applications thus far is returned, and any remaining members in the other
|
||||
forms are ignored.
|
||||
|
||||
For example,
|
||||
|
||||
```
|
||||
(zip + '(1 2 3) '(4 5 6))
|
||||
;; => ((+ 1 4) (+ 2 5) (+ 3 6))
|
||||
```
|
||||
|
||||
It's important to note that zip operates on forms, and that the form
|
||||
returned by zip may not be evaluable by itself. For instance, to actually
|
||||
transform the result in the example above into something Carp can
|
||||
evaluate, we need to wrap each member of the list in a `do`:
|
||||
|
||||
```
|
||||
(append (list 'do) (zip + '(1 2 3) '(4 5 6)))
|
||||
;; => (do (+ 1 4) (+ 2 5) (+ 3 6))
|
||||
(eval (append (list 'do) (zip + '(1 2 3) '(4 5 6))))
|
||||
;; => 9 ;; do returns the value of the last form in its body
|
||||
```")
|
||||
(defndynamic zip [f :rest forms]
|
||||
(zip-internal f forms (list)))
|
||||
|
||||
(doc map
|
||||
"Applies a function `f` to `forms` and returns a list dynamic data literal
|
||||
containing the result of the function applications. If a single form is
|
||||
provided, the function is applied to each member of the form. If multiple
|
||||
forms are provided, the function is applied to the members of each form in
|
||||
succession. If the members of a single form are exhuasted, the result of the
|
||||
applications thus far is returned, and any remaining members in the other
|
||||
forms are ignored.
|
||||
|
||||
For example:
|
||||
```clojure
|
||||
'(map symbol? '(a b c))
|
||||
=> (true true true)
|
||||
'(map + '(1 2 3) '(4 5 6))
|
||||
=> ((+ 1 4) (2 5) (6 3))
|
||||
'(map + '(1 2 3) '(4 5 6) '(7))
|
||||
=> ((+ 1 4 7))
|
||||
```")
|
||||
(defndynamic map [f :rest forms]
|
||||
(if (= 1 (length forms))
|
||||
(map-internal f (car forms) (list))
|
||||
(zip-internal f forms (list))))
|
||||
)
|
||||
|
||||
(defndynamic cond-internal [xs]
|
||||
|
@ -58,6 +58,16 @@
|
||||
(let [x (gensym-with 'a)]
|
||||
(list 'let (array x 1) (list '= x 1))))
|
||||
|
||||
(defmacro test-map []
|
||||
(let [mapped (Dynamic.map length '((a) (b c) (d e f)))]
|
||||
(Dynamic.and (Dynamic.and (= 1 (Dynamic.car mapped)) (= 2 (Dynamic.cadr mapped))) (= 3
|
||||
(Dynamic.caddr mapped)))))
|
||||
|
||||
(defmacro test-zip []
|
||||
(let [zipped (Dynamic.zip array '('a 'd) '('c 'o) '('e 'g))]
|
||||
(Dynamic.and (= 'ace (Symbol.join (eval (Dynamic.car zipped))))
|
||||
(= 'dog (Symbol.join (eval (Dynamic.cadr zipped)))))))
|
||||
|
||||
(deftest test
|
||||
(assert-true test
|
||||
(test-let-do)
|
||||
@ -221,4 +231,10 @@
|
||||
(assert-true test
|
||||
(test-gensym)
|
||||
"gensym works as expected")
|
||||
(assert-true test
|
||||
(test-map)
|
||||
"map works as expected")
|
||||
(assert-true test
|
||||
(test-zip)
|
||||
"zip works as expected")
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user