Open stevenhuyn opened 10 months ago
Tried doing this myself, defining NOT
and OR
, with
TRUE := λab.a
FALSE := λab.b
NOT:=λa.a FALSE TRUE
OR := λab.a TRUE b
This one copied from your issue above,
AND:=λab.NOT(OR(NOTa)(NOTb))
Seems to give me a proper AND
:
> AND FALSE FALSE
λa.λb.b <function, church numeral 0, church boolean false>
> AND FALSE TRUE
λa.λb.b <function, church numeral 0, church boolean false>
> AND TRUE FALSE
λa.λb.b <function, church numeral 0, church boolean false>
> AND TRUE TRUE
λa.λb.a <function, church boolean true>
Trying this out with NOT:=λa.λb.λc.acb
, I can recreate the issue you've found. Something's very fishy indeed.
Ah my bad on not giving the other defs
Reproducing fails with NOT:=λm.λn.λo.mon
, which should be equivalent. Some naming issue pops up
These should be equivalent, but they execute differently:
Yikes, dependent on both a name collision in the automated alpha conversion and the target variable. (Middle is incorrect)
Reproducing in the spec: https://github.com/evinism/lambda-explorer/pull/127
Let's analyse it, under the preface that I didn't actually run any of this and it's purely manual code analysis.
To be on the same page, the issue is that spec occurs with this piece of AST:
{
type: "function",
argument: "b",
body: { type: "variable", name: "b" },
}
This function does not contain $\epsilon_1$, so this will not lead to any collisions, but the $\alpha$-reduction cannot know this ahead of time. (Or at least, shouldn't have to.) (The case where it does contain $\epsilon_1$ is actually already handled correctly.)
In the capture avoidance step it then has to generate a new name. As far as the system is concerned, the free variables are:
const freeInReplacer = ["b"]; // The outer variable that the lambda is applied to.
const freeInExpressionBody = ["b"]; // This is actually not true, but irrelevant to this specific issue.
const argNames = [];
Using these variables, it thinks the first safe free name is $\epsilon_1$, so changes the alphaSafeExpression
to
{
type: "function",
argument: "ε₁",
body: { type: "variable", name: "ε₁" },
}
However, the resursive application of the reduction on the body of the alphaSafeExpression
then updates the $\epsilon_1$ value.
So in addition to the three cases already mentioned, there is a fourth requirement that arguments should not change to the name you are trying to replace. As mentioned, if that name does occur in the body of the function it already works; it will appear in the freeInExpressionBody
array.
// 4: isn't the argument name that is being replaced.
I.e. solved by adding nameToReplace
into the array created here:
let newName = generateNewName(
freeInReplacer.concat(freeInExpressionBody, argNames, [nameToReplace])
);
It may also be solved by not doing any replacement at all if nameToReplace
does not occur in the freeInExpressionBody
array.
freeInExpressionBody
is wrong?
Thank you for the project! Was super fun!
I tried defining AND in terms of NOT and OR and my definition does not seem to work. I believe this to be a bug? https://en.wikipedia.org/wiki/De_Morgan's_laws