Julia: Unicodes \forall and \exists not working

Created on 18 Oct 2016  ยท  40Comments  ยท  Source: JuliaLang/julia

julia> parse("โˆ€x(cat(x) โ†’ mammal(x))")

LoadError: ParseError("invalid character "โˆ€"")
while loading In[16], in expression starting on line 2

in parse at parse.jl:180
in parse at parse.jl:190

julia> parse("โˆƒx(sister(x,Spot) & cat(x))")

LoadError: ParseError("invalid character "โˆƒ"")
while loading In[15], in expression starting on line 7

in parse at parse.jl:180
in parse at parse.jl:190

All 40 comments

We do not currently parse these. I suppose we could parse them as macro calls to allow customization, or parse them as something that calls the all function. What would you propose?

Kevin wants this to work: parse("โˆƒx( sister(x,Spot) & cat(x) )")

What's the official representation of negation (~ or !)?

Sarnoff, if I follow the unicode list, it should actually be parse("โˆƒx(sister(x,Spot) โˆง cat(x))")

A macro call with the all function seems appropriate for forall and a macro call with logical or for exists. Should I do it? Where is this all function defined? Does it just include all x in X?

It means they can be tab-completed to unicode characters by typing \forall<tab> in the REPL and editor modes. Many, many characters are supported in this way, including some emoji. Characters you can type are a superset of characters that can be parsed, which are in turn a superset of characters with default meanings.

Of course we can make these parseable, we just need to decide exactly what the syntax is and what it parses to.

~ and ! are functions, so they parse to function calls. An expression like โˆƒ x f(x) presumably can't just be a function call; in particular it might bind x.

Okay, so why not start with parse("โˆ€x(cat(x) โ†’ mammal(x))")? For any object x, if x is a cat then x is a mammal.

We need a proposal for the exact grammar of that form (i.e. which non-terminals are used to parse each subexpression), and the structure that it parses to in terms of Expr objects.

It looks like it would suffice, and be simplest, for โˆ€ to parse as a unary macro (like a unary operator except as a macrocall instead of a call). I really don't think it's a good idea to make a separate parallel grammar for this logic stuff; the macro can implement those rules

That disallows โˆƒ x f(x) but โˆƒ x(f(x)) is not that much worse.

โˆ€ and โˆƒ are not parsed because it was not clear to me (when I initially expanded the list of parsed math symbols) whether they should be identifiers, operators, macros, etcetera, and what their precedence should be. Once we make a decision, it is hard to change, so it is best not to make a choice casually here.

I think parsing them as binary operators is not sensible. Eliminating that possibility eliminates the need to choose a precedence.That leaves the following options:

  1. just allow them as identifier characters
  2. parse them as standard unary operators
  3. parse them as unary macros (like if ~ were parsed like a unary operator instead of a binary one)
  4. parse them as if they were macrocalls directly, i.e. as if the token โˆ€ was basically equivalent to @forall.

1 seems dubiously useful to me. Chances are that these operators will only be useful within specific DSLs anyway, so 2 is acceptable for most cases even without macro parsing. 3 is strictly more flexible than 2 but is also strictly more complex. 4 is interesting but, I would venture, extremely hard to get right. Only 4 allows constructs like โˆ€ x f(x), but at the expense of such constructs being extremely brittle.

Note that all of 2, 3, and 4 allow โˆ€ x -> f(x). 3and 4 allow accessing the body and arguments of such an expression. That way seems preferable for expressing something like a universal or existential type, or a logical statement.

One argument for 2 is that none of the other binary connectives (and unary negation) operators, nor any of the operators that could look like implication, are parsed as macros. So most consumers of this syntax will require @type(X -> Y) anyway, and then parsing these operators as easily as possible (as simple function calls) will be most easily useful.

Yes, parsing them as unary operators seems the most sensible and conservative choice. However, I'm reluctant to implement anything absent a real application (as opposed to idle speculation on the mailing list).

Kenya.jl seems to only consist of empty files?

I need FOL before anything else.

In any case, FOL will be around for a long time, and there will probably be many others after me wondering if FOL can be used with Julia. I think ultimately I would like to see FOL being used with Julia, on the blockchain.

There are many different representation schemes in use in AI, some of which we will discuss in later chapters. Some are theoretically equivalent to first-order logic and some are not. But first-order logic is universal in the sense that it can express anything that can be programmed. _We choose to study knowledge representation and reasoning using first-order logic because it is by far the most studied and best understood scheme yet devised._ Generally speaking, other proposals involving additional capabilities are still hotly debated and only partially understood. Other proposals that are a subset of first-order logic are useful only in limited domains. Despite its limitations, first-order logic will be around for a long time. - Peter Norvig, AIMA book, p. 186

You don't need a special symbol to write code; you can always use ASCII symbols like forall. Vaporware is not a real application.

MLN already exists http://i.stanford.edu/hazy/felix/. You're right, I don't need a special symbol to code.

@hpoit, by real application I mean practical (non-vaporware) code _in Julia_ with a clear reason why and how the code clarity would benefit from using โˆ€, and what parsing semantics would be required to enable this.

Felix is written in Java and hence presumably does not (cannot) use โˆ€, so it hardly explains why/how โˆ€ parsing is needed. Nor does general mathematical-logic usage of โˆ€ help โ€” that is very different from explaining how โˆ€ should be used _in a programming language_.

I just want to use the least verbose notation possible for the universal and existential quantifiers, that's all, and this value appears to be in line with Julia. Since you and Bezanson offered to help given that I propose an exact grammar in terms of Expr objects to structure a tree, I will look into it and come back to this issue when I have it, or if I'm in doubt. How does that sound?

If you have a good proposal (that Jeff and Steven like too), I can implement it. (Though, I think the best implementation is just adding these operators to the unary operator list, but if you have other ideas, all the better.) I have a (barely started) FOL package that I've been meaning to work on sometime soon, and this will help with the syntax for me also.

Magnifique

Does the proposal have to be or would make easier if it was of Type 1 Grammar, like I understand is Julia? Apparently, MLN by default uses Type 2, probabilistic CFG, and doesn't offer an exact grammar in the sense that Bezanson mentioned, but learns the grammar from a corpus of texts.

In Markov logic, universal quantifiers are also probabilistic.

Steven, there's some info here on parsing semantics like you asked, slide 84.

@hpoit from a quick glance, that slide show contains nothing relevant. Semantic parsing is beyond the scope of the Julia parser.

Is this helpful see section 4?

@JeffreySarnoff, no. We know what the symbols mean in mathematics and logic; that's separate from the question of how they are parsed.

We can all just assume they will be parsed as unary operators, right Steven?

As I already said above, other operators parse as function calls but these might not: โˆ€ x p might introduce a new binding for x, so you can't just evaluate x and pass it to โˆ€ like you would for most operators (since x might not exist yet). In any case, moving forward with this requires considerably more detail.

My suggestion was to require โˆ€ x -> p to introduce bindings, which allows these to be parsed as simple unary functions or macros. Is there actually a benefit in allowing the whitespace delimited form?

The whitespace-delimited form is closer to standard notation. But if โˆ€ is strictly a single-argument unary operator, โˆ€x(f(x)) can still parse since the argument is a function call x(f(x)). Syntactically that's workable, but a bit odd since the argument is likely not intended to be a function call.

I intend to build Isabelle.jl to introduce Isabelle/HOL formal methods to Julia. Isabelle.jl should reuse Julia's compiler parsing and type-checking front-end to subsequently derive verification conditions to be solved by Isabelle.

References
https://isabelle.in.tum.de/
https://arxiv.org/pdf/1607.01539.pdf (in our case from Isabelle to Julia; I don't believe from Scala to Isabelle makes any sense)
https://www.isa-afp.org/entries/FOL_Harrison.shtml

With this, I'm not sure this issue is relevant any longer. The formal representation of these quantifiers should be in Isabelle, which will then be mapped to native Julia.

We should still eventually decide what to do with these symbols in the parser.

I will check out what Isabelle does with them and post it here.

On Feb 27, 2017, at 09:39, Steven G. Johnson notifications@github.com wrote:

We should still eventually decide what to do with these symbols in the parser.

โ€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

In case you missed this (as I haven't heard from anyone in here), perhaps this is also a good reference to understanding what to do with quantifiers in the parser: Julia was used in the development of ACAS-X and a theorem prover was used to guarantee properties of the code. Maybe they've already figured it out. I will eventually come back to this.

I did find myself wanting to write assertions in this style for some Pkg3 code:

@assert โˆ€ v โˆˆ inc โˆƒ p โˆˆ pairs p[1] โ‰ฒ v โ‰ฒ p[2]
@assert โˆ€ v โˆˆ exc โˆ„ p โˆˆ pairs p[1] โ‰ฒ v โ‰ฒ p[2]

Of course, what I actually ended up writing was not too terribly far away from that:

@assert all(any(p[1] โ‰ฒ v โ‰ฒ p[2] for p โˆˆ pairs) for v โˆˆ inc)
@assert all(!any(p[1] โ‰ฒ v โ‰ฒ p[2] for p โˆˆ pairs) for v โˆˆ exc)

I'm not sure if that's suggestive of a useful syntax, but that's what I wanted to write.

+1

+1 too -- I think this would allow for some very expressive syntax

Was this page helpful?
0 / 5 - 0 ratings