Call an anonymous function with a local variable as parameter
Asked Answered
M

1

5

I have a function that creates an object (in this case, a Hammerspoon Notify object), and I would like to pass this object as the parameter to an anonymous function that is itself an argument to a function call.

That's a very convoluted explanation, but I think an example makes it pretty clear.

function main()
    local n = hs.notify(...)
    print(n)          -- `hs.notify: Title (0x7fbd2b5318f8)`
    hs.timer.doAfter(1, function(n)
        print(n)      -- nil
        n:withdraw()  -- error: attempt to index a nil value (local 'n')
    end)
end

The output of this is that n prints fine the first time (hs.notify: Title (0x7fbd2b5318f8)), but is nil the second time, inside the anonymous function, and it throws an error: attempt to index a nil value (local 'n').

This sort of makes sense, because I'm never really passing it in. Is there a way to pass it in? The signature of the hs.timer.doAfter call is: hs.timer.doAfter(sec, fn) -> timer (http://www.hammerspoon.org/docs/hs.timer.html#doAfter)

Moyers answered 16/7, 2016 at 2:37 Comment(1)
Is there a good IDE or static code analyzer available to you? If you took away the first (or all) print(n) statement, a good IDE would warn you that you have assigned n a value that is never used, which would help you detect the hiding that Kurt explains in his answer.Orelee
I
9

The definition of the anonymous function includes the declaration of an argument named n, which hides the n variable from the outer scope. The function declaration is creating a new local variable that is nil unless an argument is actually passed into the function, but the timer function that calls your anonymous function isn't expecting to pass anything in, so the function-local n stays nil.

You can fix it by simply removing the argument declaration from the anonymous function, but keep the usage of n within the function. Then it will capture the n variable from the outer scope, which has the value returned from hs.notify(...).

function main()
    local n = hs.notify(...)
    print(n)          -- `hs.notify: Title (0x7fbd2b5318f8)`
    hs.timer.doAfter(1, function() -- <== no argument
        print(n)      -- nil
        n:withdraw()  -- error: attempt to index a nil value (local 'n')
    end)
end
Intuitive answered 16/7, 2016 at 3:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.