~technomancy/fennel

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
2 2

[PATCH] Make hashfn arguments work when beginning multisyms

Details
Message ID
<20190830001652.24982-1-benaiah@mischenko.com>
DKIM signature
missing
Download raw message
Patch: +17 -3
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
---
 fennel.lua | 15 ++++++++++++---
 test.lua   |  5 +++++
 2 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/fennel.lua b/fennel.lua
index f68a38e..3f1a5d0 100644
--- a/fennel.lua
+++ b/fennel.lua
@@ -638,11 +638,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 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/test.lua b/test.lua
index 2c627c3..78d96b1 100644
--- a/test.lua
+++ b/test.lua
@@ -295,6 +295,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
@@ -303,6 +305,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",
    },
    match = {
        -- basic literal
-- 
2.23.0.rc1
Details
Message ID
<8736hjqql0.fsf@hagelb.org>
In-Reply-To
<20190830001652.24982-1-benaiah@mischenko.com> (view parent)
DKIM signature
missing
Download raw message
Benaiah Mischenko <benaiah@mischenko.com> writes:

> +        -- Immediately returned argument
> +        ["(+ (#$ 1) (#$2 2 3))"]=4,

I like that we have #$ as a shorthand for the identity function; that
could come in handy for higher-order functions.

> +        -- Multisyms containing $ arguments
> +        ["(#$.foo {:foo :bar})"]="bar",
> +        ["(#$2.foo.bar.baz nil {:foo {:bar {:baz :quux}}})"]="quux",

This is very reminiscent of how Clojure treats its keywords as
functions. It's slightly more verbose, but it has the advantage of
supporting nested lookup for free. It also feels conceptually consistent
with the #$ notation above. Clojure's hashfn notation is a lot more
limited in that it requires parens after the hash, which trips a lot of
people up, and in retrospect feels like a very arbitrary rule. TBH this
makes hashfn actually seem a lot more appealing to me.

I'm in favor of this but would like to hear what others think.

If we decide to proceed we'd need to describe it in reference.md. I think
that's the only place hashfn is currently documented. A mention in the
changelog would also be warranted.

-Phil
Details
Message ID
<87v9ufyw9o.fsf@hector.i-did-not-set--mail-host-address--so-tickle-me>
In-Reply-To
<8736hjqql0.fsf@hagelb.org> (view parent)
DKIM signature
missing
Download raw message
Phil Hagelberg <phil@hagelb.org> writes:

> I like that we have #$ as a shorthand for the identity function; that
> could come in handy for higher-order functions.

There's a whole suite of common functional programming utilities that
get simple and regular shorthands due to hashfns:

- identity: #$

- thunks: to make any value a thunk, prefix with #

- pick: #$N for N 1-9. With future hashfn-vararg support, #(. [...] N)
  for any N (perhaps not ideal due to an extra allocation)

- getters: #$.property-name

- compose: #(-> $ fn1 fn2)

There are probably more examples but these come to mind quickly.

> This is very reminiscent of how Clojure treats its keywords as
> functions.

Yeah, this patch was spurred by me trying to figure out the best
zero-runtime way of porting that feature to Fennel.

> If we decide to proceed we'd need to describe it in reference.md. I think
> that's the only place hashfn is currently documented. A mention in the
> changelog would also be warranted.

Roger - I'll expand the documentation for hashfns, make a changelog
entry, and add those changes to the patch.

- benaiah
Review patch Export thread (mbox)