Carp/test/introspect.carp

72 lines
1.6 KiB
Plaintext
Raw Normal View History

(load "Test.carp")
(use-all Test Introspect)
(defn foo [x] x)
Add a proxy macro for generating functions for higher-orders It's a fairly common pattern in Carp to call a higher-order function on some structure of values, such as an Array. However, these structures, and their members, all have lifetimes under Carp's memory management model, which means they expect functions that are mapped over them to take *a reference to a value* rather than a pure value. Array.reduce is one example of such a "referential" higher-order, the type of its function argument is: ``` (Fn [a, (Ref b c)] a) ``` That is, this function takes some pure initial value, then expects to be called against the members of an array, which are *references* to the values that are alive throughout the Array's lifetime. However, one often wants to use a function that operates on pure values in such contexts, such as +, which forces the programmer to write anonymous functions that handle copying referenced values to pass them to the underlying "pure" function: ``` (Array.reduce &(fn [x y] (+ x @y)) 0 &[1 2 3]) ``` So, in using some high-order function over some structure in Carp one usually has to do two things: 1. Wrap the function in a ref 2. Handle copying references into values in order to pass them into some simpler function that can also be used outside of memory-bound contexts. The `proxy` macro captures this pattern. It wraps a given function in a referenced anonymous function and copies an argument of that function at a designated position before calling the underlying function. For example, with `proxy`, the above example becomes: ``` (Array.reduce (proxy + 2) 0 &[1 2 3]) ``` The macro effectively gives a name to a common pattern--typically it will only save the programmer a few characters, but it perhaps makes the act of using a "function that doesn't care about references" in a reference dominant context more apparent. One can also use the macro to develop more specialized macros for certain higher-orders, since these usually dictate where copying must be performed. For instance, the `Array.reduce` function argument always expects the referenced value to occur in the second position, thus one could write: ``` (defmacro reducer [function] (eval (list proxy function 2))) ``` Then the above code becomes even simpler: ``` (Array.reducer (reducer +) 0 &[1 2 3]) ``` Which roughly means, "use the + function (which has no concept of references) in this reference dependent context". N.B. The examples using `+` won't work as of now due to current bugs related to calling `arity` directly on an interface--but a synonym for plus `add` defined as an explicit function will make all the above work as expected.
2020-08-08 00:12:43 +03:00
(defn add [x y] (+ x y))
(def bar 2)
(deftype Foo [x Int])
(deftype Bar (Of [Int]))
(definterface baz (Fn [a] a))
(defmodule Qux (defn id [x] x))
(defmacro test-function? [x]
(eval (list 'Introspect.function? x)))
(defmacro test-variable? [x]
(eval (list 'Introspect.variable? x)))
(defmacro test-module? [x]
(eval (list 'Introspect.module? x)))
(defmacro test-struct? [x]
(eval (list 'Introspect.struct? x)))
(defmacro test-sumtype? [x]
(eval (list 'Introspect.sumtype? x)))
(defmacro test-interface? [x]
(eval (list 'Introspect.interface? x)))
(defmacro test-arity [x]
(eval (list 'Introspect.arity x)))
(defmacro test-arguments [x]
(collect-into
(map
(fn [arg] `(copy %(str arg)))
(eval `(Introspect.arguments %x)))
array))
(deftest test
(assert-true test
(test-function? foo)
"function? works as expected")
(assert-true test
(test-variable? bar)
"variable? works as expected")
(assert-true test
(test-struct? Foo)
"struct? works as expected")
(assert-true test
(test-sumtype? Bar)
"sumtype? works as expected")
(assert-true test
(test-interface? baz)
"interface? works as expected")
(assert-true test
(test-module? Qux)
"module? works as expected")
(assert-equal test
&[@"x"]
&(test-arguments foo)
"arguments works as expected")
(assert-equal test
1
(test-arity foo)
"arity works as expected")
Add a proxy macro for generating functions for higher-orders It's a fairly common pattern in Carp to call a higher-order function on some structure of values, such as an Array. However, these structures, and their members, all have lifetimes under Carp's memory management model, which means they expect functions that are mapped over them to take *a reference to a value* rather than a pure value. Array.reduce is one example of such a "referential" higher-order, the type of its function argument is: ``` (Fn [a, (Ref b c)] a) ``` That is, this function takes some pure initial value, then expects to be called against the members of an array, which are *references* to the values that are alive throughout the Array's lifetime. However, one often wants to use a function that operates on pure values in such contexts, such as +, which forces the programmer to write anonymous functions that handle copying referenced values to pass them to the underlying "pure" function: ``` (Array.reduce &(fn [x y] (+ x @y)) 0 &[1 2 3]) ``` So, in using some high-order function over some structure in Carp one usually has to do two things: 1. Wrap the function in a ref 2. Handle copying references into values in order to pass them into some simpler function that can also be used outside of memory-bound contexts. The `proxy` macro captures this pattern. It wraps a given function in a referenced anonymous function and copies an argument of that function at a designated position before calling the underlying function. For example, with `proxy`, the above example becomes: ``` (Array.reduce (proxy + 2) 0 &[1 2 3]) ``` The macro effectively gives a name to a common pattern--typically it will only save the programmer a few characters, but it perhaps makes the act of using a "function that doesn't care about references" in a reference dominant context more apparent. One can also use the macro to develop more specialized macros for certain higher-orders, since these usually dictate where copying must be performed. For instance, the `Array.reduce` function argument always expects the referenced value to occur in the second position, thus one could write: ``` (defmacro reducer [function] (eval (list proxy function 2))) ``` Then the above code becomes even simpler: ``` (Array.reducer (reducer +) 0 &[1 2 3]) ``` Which roughly means, "use the + function (which has no concept of references) in this reference dependent context". N.B. The examples using `+` won't work as of now due to current bugs related to calling `arity` directly on an interface--but a synonym for plus `add` defined as an explicit function will make all the above work as expected.
2020-08-08 00:12:43 +03:00
(assert-equal test
6
(Array.reduce (Introspect.with-copy add 1) 0 &[1 2 3])
"with-copy works as expected")
)