Arc Forumnew | comments | leaders | submitlogin
New-found incompatibility: passing nil to a macro?
2 points by zck 186 days ago | 19 comments
Using this macro and function:

    (def nil-test-fn (arg)
      (prn "arg is " (if arg "not " "") "nil."))
    
    (mac nil-test-mac (arg)
      (prn "arg is " (if arg "not " "") "nil."))
In both of these, I expect to see "arg is nil?" printed out.

In Arc3.2, passing nil works as expected:

    arc> (nil-test-fn nil)
    arg is nil.
    "arg is "

    arc> (nil-test-mac nil)
    arg is nil.
    "arg is "
And in anarki, the function is fine:

    arc> (nil-test-fn nil)
    arg is nil.
    "arg is "
But the macro seems to think it's not nil!

    arc> (nil-test-mac nil)
    arg is not nil.
    "arg is "
I suspect that it's related to the zeroth incompatibility, where `nil` is `()`, but I'm not sure exactly what to make of it. Anyone have any ideas?


2 points by akkartik 186 days ago | link

Yeah, it seems pretty clear this is related to `nil` vs `()`. This is pretty neat. Some implications:

    arc> (def f (x) (prn x (if x " truthy" " falsy")))
    arc> (f nil)
    () falsy

    arc> (mac m (x) (f x))
    arc> (m nil)
    nil truthy
What's more, the problem is within macroexpansion, the part that conceptually should be the same between functions and macros:

    arc> (macex1 '(m nil))
    nil truthy
Here's the definition of `ac-macex` in ac.rkt:

    (define (ac-macex e . once)
      (if (pair? e)
          (let ([m (ac-macro? (car e))])
            (if m
                (let ([expansion (apply m (cdr e))])
                  (if (null? once) (ac-macex expansion) expansion))
                e))
          e))
I think the discrepancy lies somewhere between `ac-global-call` for calling functions and using a direct `apply` for macros.

-----

2 points by zck 185 days ago | link

Yeah, just to make sure, I `git bisect`ed it down, and the behavior changed at this commit: https://github.com/arclanguage/anarki/commit/b321fb0c0273d1c...

-----

2 points by akkartik 185 days ago | link

Oh nice. Thank you.

-----

2 points by zck 182 days ago | link

Other weird thing I'm seeing:

Arc3.2:

    arc> (withs nil 3)
    3
Anarki:

    arc> (withs nil 3)
    Can't take car of nil
      context...:
       /usr/share/racket/collects/racket/private/kw.rkt:594:14:  withs
       /home/zck/programs/arc/anarki/ac.rkt:647:0: ac-call
       /home/zck/programs/arc/anarki/ac.rkt:1398:4
       eval-one-top12
This is a minimal example from something I found in unit-test.arc. It's some macros related to setup code -- if there's no setup, I currently generate something like `(withs nil 3)`. But that errors in Anarki.

You can explicitly pass no arguments. In Anarki:

    arc> (withs () 3)
    3

-----

2 points by akkartik 181 days ago | link

At least to me, this is expected. The commit you pointed out above switched the null value to '(). The symbol `nil` still evaluates to (). But the need for evaluation implies that it isn't available in contexts that are not evaluated, such as function arguments or in this particular slot of `withs`.

Like I said, happy to revert it if you don't like it. The whole thing came up because of this conversation: https://github.com/arclanguage/anarki/pull/145#issuecomment-.... The motivation was to simplify the Arc implementation. We already have a nil representation in the underlying Racket; it seems unnecessary to so bend over backwards to switch it to something else.

-----

2 points by zck 181 days ago | link

Let me figure out where in the ecosystem I'm getting the nil value from. I suspect I need to switch a bunch of usages of nil in my codebase to () or '().

-----

3 points by akkartik 181 days ago | link

Then again, who knows how long it will take to fix this problem. I'll roll back for now.

Edit: I feared that rolling back may also be non-trivial, but there were only some minor conflicts. Do a `git pull`! All tests are passing, and the bugs here should be fixed now.

Sorry about all this confusion and back-and-forth spanning a year. I'm going to back off on this change now. I think none of us have the bandwidth for a change this radical.

-----

2 points by zck 177 days ago | link

Yeah, that seems better. I'm still tracking down two test failures, but they're not because of this. I think templates now are of type 'tem, not type 'table.

I tried to make some changes to () instead of nil, and I was not a big fan of how it looked. I found it very unusual that unless quoted, parentheses mean function application. Letting () be the way to write the empty list (and I believe it worked differently quoted from unquoted, but I'm not sure offhand) completely breaks my mental model of how Lisps are parsed.

But with the revert, things look good. Thanks.

-----

2 points by akkartik 177 days ago | link

Great.

What are the two test failures in?

-----

3 points by zck 177 days ago | link

As it turns out, it's one test failure (not sure how I got the data into a weird state). It's in the test `tests-are-wrapped-to-create-test-result-template`: https://hg.sr.ht/~zck/unit-test.arc/browse/tests.arc?rev=def...

It's failing because `(type (inst 'foo))` is different in Anarki than Arc. It's a simple change to make it work; I just want to do two things before I stop looking at it:

1. Look deeper into the template inconsistencies. Thanks for the files about this in Anarki. 2. Decide if I want to cut support for Arc, or make this code work in both. This might just involve killing the test, as it's not the _most_ useful test.

-----

2 points by akkartik 177 days ago | link

Ouch, have the tests for unit-test.arc been failing for the past year? :( :( Very sorry about that. I see the failure now.

I somehow forgot that unit-test.arc has its own tests. Could you post the instructions for running the tests in the Readme? That would also have the salubrious side effect of showing people a way to run a bunch of existing tests.

Edit: I've added some instructions for running unit-test.arc tests to the Anarki readme: https://github.com/arclanguage/anarki/commit/0913288ec1477f2.... Hopefully that'll help remind me.

-----

2 points by zck 176 days ago | link

Don't worry about it! I haven't even run them in a long time, until this week -- I figured I could get teardown functionality working, then remove the hack around running Anarki tests (https://github.com/arclanguage/anarki/blob/master/tests.arc#...).

Thanks for adding the instructions.

-----

3 points by rocketnia 173 days ago | link

Once that last one is passing (or maybe even before it's passing), should the top-level tests.arc run these tests too? That way this can be caught not only by Travis CI, but also by people running tests.arc according to the readme.

-----

2 points by zck 173 days ago | link

Good question. I guess it's a question of if we have enough Anarki tests to minimze breaking changes.

If we do, then I don't know if it matters if we run unit-test.arc tests -- it's just one Arc library, presumably of several.

It's a little different than other libraries because it's what we use for Anarki unit tests.

Running it as part of Anarki's unit tests would prevent breakage, and is a simple solution to get a lot more tests added to the language. Anyone want to write a bunch of tests for Anarki itself?

-----

3 points by akkartik 173 days ago | link

Anarki isn't really intended to avoid or minimize breaking changes. The unit tests verify only that everything is internally consistent. That boundary around 'internal' should include unit-test.arc, I think.

-----

3 points by krapp 178 days ago | link

Is the problem the equality between nil and the empty list, or did that just expose some unknown flaw with the way Arc deals with macros?

-----

3 points by zck 175 days ago | link

I _think_ it's some weirdness with the nil/empty list thing. I was getting a case where (str x) resulted in the string "nil", but whatever that object was was not treated as nil, for example in conditionals.

-----

2 points by akkartik 182 days ago | link

If I found a way to "roll forward" and lose all trace of nil from the language, would that be acceptable?

(I tried briefly but haven't managed it yet. So it's probably faster to just roll back. I'm just curious about the question.)

-----

2 points by zck 181 days ago | link

Interesting concept. I feel like I'd be ok with it? I want to say we should bind `nil` to `'()`, so existing code would continue to work, but I might be overindexing on compatability and what I'm used to.

I will admit to not being super sure what the real differences between nil and '() are. Presumably it's more than "what is the human-readable representation of the value that terminates a list/is the false value". But I'm not sure what. Also, is there a difference between the quoted and unquoted version? It feels odd to write () in a repl unquoted -- usually, I expect parens to mean a function or macro call.

Prior discussions I've found:

* https://github.com/arclanguage/anarki/pull/145#issuecomment-... * http://arclanguage.org/item?id=11723 (ten years ago Tuesday!) indicates using '() helps with Racket interop * http://arclanguage.org/item?id=21047

-----