~technomancy/fennel

Make hashfn arguments work when beginning multisyms v2 PROPOSED

Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~technomancy/fennel/patches/7890/mbox | git am -3
Learn more about email & git

[PATCH v2] Make hashfn arguments work when beginning multisyms Export this patch

Converts $ arguments to $1 when they begin a multisym (matches the
behavior of a lone $).

Sets used-local metadata correctly for symbols which begin
multisyms. This was previously incorrect for all symbols which started
multisyms, but the hashfn argument bug fixed in this commit is the
only known effect.

Added test cases for multisyms and immediately-returned arguments to
ensure neither regress.

Enables a getter function shorthand: #$symbol.property

---

This version of the patch is rebased onto master, and should now merge
cleanly.

 changelog.md |  1 +
 fennel.lua   | 17 +++++++++++++----
 reference.md |  9 +++++++--
 test.lua     |  5 +++++
 4 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/changelog.md b/changelog.md
index bc88636..00c45fd 100644
--- a/changelog.md
+++ b/changelog.md
@@ -19,6 +19,7 @@
 * **Disallow** `~` in symbols
 * **Remove** `~=` special; use `not=` instead
 * Add `hashfn` and `#` reader macro for shorthand functions like `#(+ $1 $2)`
+* Allow hashfn arguments to be used in multisyms
 * Add `macro` to make defining a single macro easier
 * Add `(comment)` special which emits a Lua comment in the generated source
 * Allow lua-style method calls like `(foo:bar baz)`. **Disallow** `:` in symbols.
diff --git a/fennel.lua b/fennel.lua
index b07b47e..21ab5e3 100644
--- a/fennel.lua
+++ b/fennel.lua
@@ -658,11 +658,20 @@ end
 -- if they have already been declared via declareLocal
 local function symbolToExpression(symbol, scope, isReference)
     local name = symbol[1]
-    if scope.hashfn and name == '$' then name = '$1' end
-    local parts = isMultiSym(name) or {name}
-    local etype = (#parts > 1) and 'expression' or "sym"
+    local multiSymParts = isMultiSym(name)
+    if scope.hashfn then
+       if name == '$' then name = '$1' end
+       if multiSymParts then
+          if multiSymParts[1] == "$" then
+             multiSymParts[1] = "$1"
+             name = table.concat(multiSymParts, ".")
+          end
+       end
+    end
+    local parts = multiSymParts or {name}
+    local etype = (#parts > 1) and "expression" or "sym"
     local isLocal = scope.manglings[parts[1]]
-    if isLocal and scope.symmeta[name] then scope.symmeta[name].used = true end
+    if isLocal and scope.symmeta[parts[1]] then scope.symmeta[parts[1]].used = true end
     -- if it's a reference and not a symbol which introduces a new binding
     -- then we need to check for allowed globals
     assertCompile(not isReference or isLocal or globalAllowed(parts[1]),
diff --git a/reference.md b/reference.md
index 19a54f3..94b87b0 100644
--- a/reference.md
+++ b/reference.md
@@ -65,8 +65,9 @@ This style of anonymous function is useful as a parameter to
 higher order functions, such as those provided by Lua libraries
 like lume and luafun.
 
-The current implementation only allows for functions of up to
-9 arguments, each named `$1` through `$9`.
+The current implementation only allows for functions of up to 9
+arguments, each named `$1` through `$9`. A lone `$` in a hash function
+is treated as an alias for `$1`.
 
 Hash functions are defined with the `hashfn` macro, which wraps
 it's single argument in a function literal. For example, `#$3`
@@ -74,6 +75,10 @@ is a function that returns it's third argument. `#[$1 $2 $3]` is
 a function that returns a table from the first 3 arguments. And
 so on.
 
+Hash arguments can also be used as parts of multisyms. For instance,
+`#$.foo` is a function which will return the value of the "foo" key in
+its first argument.
+
 ### `partial` partial application
 
 Returns a new function which works like its first argument, but fills
diff --git a/test.lua b/test.lua
index 53f3bc1..cec3ef0 100644
--- a/test.lua
+++ b/test.lua
@@ -297,6 +297,8 @@ local cases = {
         ["(#(+ $3 $4) 1 1 3 4)"]=7,
         -- One argument
         ["(#(+ $1 45) 1)"]=46,
+        -- Immediately returned argument
+        ["(+ (#$ 1) (#$2 2 3))"]=4,
         -- With let
         ["(let [f #(+ $1 45)] (f 1))"]=46,
         -- Complex body
@@ -305,6 +307,9 @@ local cases = {
         ["(#(+ $ 2) 3)"]=5,
         -- Mixed $ types
         ["(let [f #(+ $ $1 $2)] (f 1 2))"]=4,
+        -- Multisyms containing $ arguments
+        ["(#$.foo {:foo :bar})"]="bar",
+        ["(#$2.foo.bar.baz nil {:foo {:bar {:baz :quux}}})"]="quux",
     },
     methodcalls = {
         -- multisym method call
-- 
2.23.0.rc1
View this thread in the archives