-
-
Notifications
You must be signed in to change notification settings - Fork 40
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
[FEATURE] Prepare an ADR on type annotations #162
Comments
Just a general question: Why is touching SANY a no-go? |
We are still in the exploration phase and it would be a bad idea to change the syntax all the time. As far as I understand, Leslie also wants to keep the syntax minimalistic (kind of). |
(I assume that the ratio of type annotations to the actual behavior spec is constant in the size of the spec) The examples above don't really look minimalistic from a reader's perspective. Our eyes haven't been trained to filter types in TLA+ specs, but the type annotations make it more difficult to understand the true meaning of the spec. Perhaps, this is Leslie's main reason to keep the language minimalistic. As a tool developer, I'm worried that types-as-strings will make it hard for IDEs or end up having poor ergonomics. By the way, how does this work with |
The purpose of these type annotations is not to have them written by the user. Though it should be possible. The goal is to have transparent type information (maybe inferred by different tools) right in the spec. Once we have integrated our new type inference, every tool developer will be able to use them, without extending the TLA+ parser or using an external library. The current approach to developing all analyses in a black box makes it harder to integrate tools at all levels. On second thought, the approach presented here is not that different from Pluscal, where the whole code is written in a huge comment. Think of these types like types in python. If you really want to write the type annotations, you can. But, ideally, it should be done by a type inference engine. I think there is really a difference between |
So tools would be expected to auto update the specs with inferred types? And/or does the user need to provide some hints? Why not follow the Pluscal convention and have these types in comments? And could you give an example of how these type annotations would differ from a TypeOk ? |
I gave it a bit of thought. Ideally, the authors don't have to write type annotations at all, like they (almost never) do in OCaml or Isabelle. So the tools should infer the types, as they need them. However, there are three reasons why, I believe, the types must be transparent:
If we are talking about an ideal scenario, I would like to have something like Java annotations or LLVM intrinsics, that is, a way to store metadata right in the code, but without introducing additional code constructs, as metadata depends on the tool. I suspect that it is quite a bit of work on the SANY parser to introduce Java-like annotations. @lemmy, what is your opinion? As for annotations in the comments, there are two problems:
So the case of Pluscal is simple. You take one language in a large comment and translate it into one TLA+ spec. Here we are talking about type annotations for individual variables, operators, bound variables, etc. Finally,
(It does not work for operators, but let's ignore that fact.) Then, I would like to have an annotation To sum up, I guess this is actually the beginning of the ADR. Sorry if the text sounds a bit too aggressive or patronizing. I just had all these things for too long in my head. Now I am happy to have them in this issue :-) |
EXTENDS Sequences
CONSTANTS Range
(* ^@type("set(z)") *)
VARIABLES list
(* ^@type("seq(z)") *)
Mem(e, es) ==
(*
^@type("<a, seq(a)> => Bool") *)
\E i, j \in DOMAIN es:
(* ^@type("Int")
^@type("Int") *)
e = es[i]
Init ==
(*
^@type("<> => Bool") *)
list = <<>>
Next ==
(*@
^@type("<> => Bool") @*)
\E e \in Range:
(*@^@type("set(z)") @*)
list' = Append(list, e) |
That looks completely different from what I expected, but the idea here is that we can introduce annotations graphically right in the comments. Somehow, github preview has a different idea of spaces than the final formatter. |
Another solution proposed by Leslie Lamport: THEOREM \A a : \A e \in a, es \in Seq(a) : Mem(e, es) \in BOOLEAN
|
Assuming I understand you correctly, I think it's a good idea to defer the how of the visualization representation of types to IDEs. E.g. the Toolbox could show the type annotations on-demand in mouse hovers and basic text editors can show them in-lined. Pretty-printed specs would simply exclude all type annotations. As to writing types when necessary, Leslie's idea of using theorems and the method of referring to subformulas would probably require IDEs to provide refactoring support for (positional) references because - contrary to TLAPS proofs - the spec is unfinished/unstable when a user writes type annotations. On the other hand, it's probably good advice to use labeled references and rely on positional ones only for "glassbox" specs. |
I like the approach of using positioned annotations in a comment below an expression. It also gives us a way to introduce all kinds of annotations, not only type annotations. Leslie's idea is interesting, as the theorem could be also proven in TLAPS, if one really wants to do that. But then the types will be far away from the place where they are used. |
About types being far away: The drawback could be alleviated by IDEs showing them in hovers/overlays and commands that navigate the cursor back and forth (similar to the PlusCal <> TLA+ mapping commands). |
As a vim user, I am a bit skeptical about putting too much trust on IDEs :-) |
It's 2020 and vim still can do something like overlays/hovers/...? :-p Teasing aside, TLA+ has several features (PCal mapping, TLAPS annotations, profiling annotations), which only exist in the Toolbox. Btw. I intentionally wrote IDEs and not "Toolbox" because all of this should be implemented via e.g. LSP in an IDE agnostic way. |
Do you think it makes sense to have TLA+ on the LSP list? Btw, there are two kinds of annotations: the user annotations (rare but possible) and output annotations. I see how LSP would work for output annotations. Leslie's approach works for both. The approach with comments also works for both, though the formatting is a bit of pain. |
I guess, vim's approach is to use folding/unfolding, which works well with comments :) |
LSP looks interesting. Maybe we do not need type annotations tightly integrated into the source code. @shonfeder, you mentioned LSP in other contexts several times. What is your take on that? |
Taking a step back, I wonder if it is really helpful to distinguish between user and output type annotations where they are shown. Won't users just care for what the type inference engine makes out of user-provided annotations? In other words, why not only ever show output annotations regardless from where the type inference engine sourced its information. Taking another step back, I would probably prefer to read the result of type inference as TLA+ expressions instead of a new syntax (what's (* ^@type("Int")
^@type("Int") *)
vs
(* i \in Int
j \in Int *) What LSP list are you referring to (note that there exists no LSP for TLA+ yet)? LSP is not a proposal on its own, but just a suggestion on how to implement any proposal in a way that is sufficiently IDE agnostic to allow for making IDE features part of a proposal. |
That is what I meant. TLA+ is not on that list ;-) |
This is an operator type. It takes two arguments (a type variable |
It looked at implementing an LSP for TLA+ last summer and there are many use cases for when an LSP would be useful. Perhaps, types are the tipping point to finally implement an LSP. |
I understand that wish, but there are two problems: (1) typically type grammars are more concise than the types-as-sets syntax, (2) it is not clear how to nicely express some types, e.g., the type of a function from Int to Int, or an operator over an Int and a high-level operator returning a Boolean. |
What I meant is that it is new syntax that increases the mental burden, which, perhaps, is fine if I only have to read it rarely but probably an obstacle if I have to type it (especially infrequently). |
For the operators, we could also use the inverse notation, as suggested by @shonfeder, e.g.: A(x, y) == "(Int, Int) => Int" :>
x + y This is actually quite similar to Isabelle/HOL. We just use |
Good catch re: typing records! fwiw, the string expressions inside a special operator -- as you're showing here -- look pretty good to me. |
As a potential user writing type annotations I kind of like that approach -- it looks pretty neat :) |
I just realized that SANY rejects assumptions over state variables, so we cannot write e.g. |
It looks like type annotations in comments are the most stable solution after all. That is, points 1.3 and 2.3 in ADR1 #179. Alternatively, we can use my hack, that is, a combination of 1.3 and 2.4, but I agree that it is more of a hack. |
Oh well... at least that helps make the decision easier! |
It's not easy to extract annotations from the comments unless we really integrate with the SANY lexer. So we might still consider the combination of TypeAxiom ==
/\ RM <: "Set(RM)"
/\ rmState <: "RM -> Str"
/\ tmState <: "Str"
/\ tmPrepared <: "Set(RM)"
/\ msgs <: "Set([type: Str, rm: RM])" In this example, |
|
I was actually quite surprised that one can only write |
TLC!Assert |
Right. Normally, one would restrict values with |
I have renamed the previous document to 001rfc-types.md, as it contains all available options. The actual ADR docs/adr/002adr-types.md contains a concrete proposal on the type grammar and type annotations. See PR #179. I already love these type annotations, even without having any type checker for them :-) |
Leslie Lamport proposes to use the proofs syntax for the higher-order operators as well: THEOREM BarType ==
ASSUME NEW G(_,_),
\A x \in Int, y \in STRING : G(x,y) \in Int
PROVE Bar(G) \in BOOLEAN Similar to that, we can write a theorem about the type of THEOREM FooType ==
\A a \in Int: \A b \in STRING: Foo(a, b) \in Int This looks like a great way of integrating the output of type inference into a TLA+ specification. Theorems like that could be used for the manual proofs with TLAPS. |
If we like to write types interactively, that is, in the process of spec writing, ADR-002 gives us a concise way to do that. |
Here is a solution to type annotations suggested by Leslie Lamport. The method This is an interesting approach and it probably increases usability in comparison to what we have in ADR-002. For instance, the user could write: ---- MODULE TypedExample ----
VARIABLE
\* type: Set(Int)
S
\* type: (Int) => Bool
Mem(e) == \E x \in S: x = e
\* type: () => Bool
Init ==
S = {}
... The syntax in the comments is quite minimalistic and still easy to parse. Interestingly, SANY is keeping comments as an array, so it should be quite easy to extract the above annotations from the comments. |
The ADR has not changed much since the discussion. I find it amazing that the type checker is working exactly as we discussed in this issue. There are two-level of annotations: (1) |
Just found that |
As a temporary solution, I will change |
The ADR has not been changing for several months. Closing the issue. |
The current approach to type annotations in Apalache is quite hacky, for several reasons:
{}
and<<>>
instead of declaring the types of variables, constants, operators, and bound variables.With the new type inference by Jure @Kukovec (to be integrated), we have to find a good way to integrate type information into TLA+ code without touching SANY (the TLA+ parser). Keeping type information on a side, e.g., in a separate file, is not a nice solution.
I see the following approach. We write our own syntax for the type system (actually, we have one already, just no parser yet) and annotate constants, variables, operators and bound names as in the following example:
It is again "convention-over-configuration". Note that types are specified as strings. What I like about it is that operators are easy to write and they are available almost everywhere. However, we should think about type annotations for set comprehensions.
The text was updated successfully, but these errors were encountered: