hydromatic / morel

Standard ML interpreter, with relational extensions, implemented in Java
Apache License 2.0
291 stars 15 forks source link

Replace `elem` operator with `in`, and `notelem` with `notin` #226

Open julianhyde opened 2 weeks ago

julianhyde commented 2 weeks ago

In #33 we added elem and notelem operators to test for list membership, for example:

from e in emps
  where e.deptno elem [10, 30]
  andalso e.job notelem ["CLERK", "MANAGER"] 

At the time, we chose not to use in because we thought it would conflict with the use of in in the from and join clauses. But since #202, the above example can be expanded to

from e
  where e elem emps
  andalso e.deptno elem [10, 30]
  andalso e.job notelem ["CLERK", "MANAGER"] 

and doesn't make much sense that e in emps changes to e elem emps.

I now believe that if we obsoleted elem and used in for both cases it would not make the grammar ambiguous, or the ambiguity could be easily resolved using parentheses, e.g.

# Expression with 'elem'
val f = from e in emps, i elem []

# ...becomes ambiguous when using `in`
val b = from e in emps, i in []

# ...but can be resolved with parentheses
val b = (from e in emps, i) in []

# ...and besides, was invalid because 'i' is not safe (has no deducible range)

The ambiguity only arises if in occurs after the from expression (and any join clauses).

from a in b in c

is unambiguously equivalent to

from a in (b in c)

because a is syntactically a pattern, not an expression, and must not contain in. The expression

let
  val b = [1]
  val c = [[0], [2]]
in
  from a in b in c
end;

should return val it = [false]: boolean list.

There is potential ambiguity due to in in the let <decls> in <expr> construct. For example, the following contains in 3 times:

let
  val administrators =
    from e in emps
      where e.job in ["MANAGER", "CLERK"]
in
  List.length administrators
end;

If we do this, we should also replace notelem with notin. (Not notIn, for reasons noted in commit 9566f8bee063bed7fe64a1015f1b66f32940811a.)