Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with Demorgan Definition of AND #125

Open
stevenhuyn opened this issue Oct 31, 2023 · 7 comments
Open

Issue with Demorgan Definition of AND #125

stevenhuyn opened this issue Oct 31, 2023 · 7 comments

Comments

@stevenhuyn
Copy link

stevenhuyn commented Oct 31, 2023

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

AND:=λab.NOT(OR(NOTa)(NOTb))
AND: λa.λb.(λa.λb.λc.acb)((λa.λb.a(λa.λb.a)b)((λa.λb.λc.acb)a)((λa.λb.λc.acb)b))
<assignment>

> AND FALSE FALSE
λb.λc.c
<function, church numeral 0, church boolean false>

> AND FALSE TRUE
λb.λc.c
<function, church numeral 0, church boolean false>

// This case here doesn't seem to evaluate properly?
> AND TRUE FALSE 
λb.λc.b
<function, church boolean true>

> AND TRUE TRUE
λb.λc.b
<function, church boolean true>(+)
@evinism
Copy link
Owner

evinism commented Jan 19, 2024

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.

@stevenhuyn
Copy link
Author

Ah my bad on not giving the other defs

@evinism
Copy link
Owner

evinism commented Jan 20, 2024

Reproducing fails with NOT:=λm.λn.λo.mon, which should be equivalent. Some naming issue pops up

@evinism
Copy link
Owner

evinism commented Jan 20, 2024

These should be equivalent, but they execute differently:

Screenshot 2024-01-20 at 12 09 17 AM

@evinism
Copy link
Owner

evinism commented Jan 20, 2024

Yikes, dependent on both a name collision in the automated alpha conversion and the target variable. (Middle is incorrect)

Screenshot 2024-01-20 at 12 17 01 AM

@evinism
Copy link
Owner

evinism commented Jan 20, 2024

Reproducing in the spec: #127

@anderium
Copy link

anderium commented Jul 12, 2024

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.

body: replace(nameToReplace, replacer, alphaSafeExpression.body),

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.

// Then we pick a new name that
// 1: isn't free in the replacer
// 2: isn't free in the expression body
// 3: isn't captured by an intermediate function in the expression body

        // 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)
);

        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.

So why did I say that freeInExpressionBody is wrong?

The variable b is obviously bound in the function. This line should probably use expression instead of expression.body.

const freeInExpressionBody = getFreeVars(expression.body).map(

Although in that case, the variable should be part of argNames, so that line should also use expression instead of expression.body. And unsurprisingly that should lead to the same result, only shifting the array in which the function argument itself occurs.

const argNames = getAllArgumentNames(expression.body);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants