-
-
Notifications
You must be signed in to change notification settings - Fork 27
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
Add check for assignment to defconst #152
Conversation
Actually the definition of the type relation should make (cl-defmethod elsa-type-accept ((this elsa-const-type) other)
(and (elsa-const-type-p other)
(elsa-type-equivalent-p (oref this type) (oref other type))
(equal (oref this value) (oref other value)))) There's probably a bug someplace else. As for the defconst, the In fact There are two options I can see:
I think option 2 is better but requires more work... basically the assignment analysis everywhere needs to be changed (probably can be abstracted into some helper function). In the future I'd also like to have something akin to javascripts |
Just to elaborate a little bit, Const type is not the same as readonly variable. We can have a type So better than the abovementioned |
If I understand correctly, you want #150 to be resolved via the type checker rather than as a separate rule (as I did). That's probably a better idea in the long run, as individual rules are harder to maintain than a generic type checking algorithm. The type checker can also take care of more situations than a single rule can.
I was suprised by this as well, I'll try to investigate. But even if Const 1 accepts Const 1, we still want to warn on assignment to a defconst. Otherwise, we should use a In that case, the type of
Yes, I saw that in the code. My understanding is that Const type is akin to value types, so it does not mean constant, but rather it's the most precise type you can assign to that value. Though as you note, it may not be helpful to always infer a Const type:
I think value types are useful when used as type annotations. So the user can voluntarily restrict a type to discrete values. When inferring it may cause more trouble than worth. It seems we are in agreement here, based on:
On that note, I haven't groked the full type algebra you are going for though. Is there a document for that somewhere? Or is it implicitly defined by the test suite?
Why not make it mean "never write to it" instead? When typechcking
|
Yep, which is why I think we actually can't solve this on the type level and the rule you wrote, or a similar one, will be necessary.
Yes exactly. I think a
Maybe we should rename it to value types. That makes equal (or more) sense and removes the const/readonly ambiguity. There's a #74 proposal so we can do that along with this change.
I haven't thought of it this way! We could probably indeed bake the flag into the type itself. So the readonly decorator would simply break typecheck when accepting anything, while when being the asigned value we would only look at the wrapped type. I like this. I wanted to allow one assignment because yes, in theory you can have initialization later, but this probably makes no sense. ISTR there was some language where this made sense, but probably porting it to elisp is non-sensical. I guess what I ment was simply that (x marked readonly) |
As for the type algebra, the WIP document is here: #96 I'm still slowly finalizing it. I'm mostly modelling this with typescript/flow in mind as JS is surprisingly similar to lisp when it comes to being dynamic language with "random objects" (i.e. lists in elisp and objects in javascript) so many problems are basically 1:1 parallels. |
In Rust (and I guess TypeScript) it makes sense to do:
where I can't think of an example in Elisp where you would need that though.
Great, I'll definitely take a look. I agree that TS/Flow are good systems to take from, since Elisp has the same "anything goes" vibe. TS also makes a good argument that unsound type systems can still be useful. I have taken a deep look at the Flow paper a few months back and implemented the type system in Rust, but I was not particularly enlightened by the approach.
Indeed, we may reuse that information if we want to have it anyway. Both ways are reasonable; what is more practical for the implementation might win. |
I've updated the PR:
I've updated the failing tests, and added a new one. Note that for one test I had to use I'm a bit surprised that an explicit type annotation will totally override the inference done by the analysis. I guess it's helpful while things are still in flux, but on the long run we may want to check that annotations are at least somewhat compatible with the inferred type. Otherwise, explicit annotations may be an easy way to introduce bugs. Here for instance, annotating as |
tests/test-analyser.el
Outdated
@@ -29,7 +29,7 @@ | |||
|
|||
(it "should analyze the default form of the defconst and assign that type" | |||
(elsa-test-with-analysed-form "(progn (defconst foo 'bar) foo)" form | |||
(expect (elsa-nth 2 form) :to-be-type-equivalent (elsa-make-type Const bar)))) | |||
(expect (elsa-get-type (elsa-nth 2 form)) :to-equal (elsa-make-type Readonly Const bar)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this also use the type equivalency check? I'm not sure if equality will match in the new Emacs versions with the structures in place of vectors.
But I think the problem is that readonly accepts nothing and so we can't really use this helper... hmm.
Maybe test that it's readonly with the predicate and then test the wrapped types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe test that it's readonly with the predicate and then test the wrapped types?
Sounds reasonable. In the long run I think it's useful to have a strict equality as well, especially for tests.
Hm, I partially thought of the annotations as backdoor to help fix "crap" while the type system is properly finished and most of all until we have all the internal/builtin functions annotated (which number in thousands) without which the type inference is quite bad. As for the defconst I'd be perfectly fine with reading the annotation, whatever it is, and wrapping it in a readonly. We will also need to add a rule that allows assiging readonly types, I don't see that (but I'm also really tired :D). I.e. (defconst foo "string")
;; (bar :: String)
(defvar bar)
(setq bar foo) ;; should type check "assignment of Readonly Const String to String", is good. |
Indeed! I added it in the top definition of |
@fmdkdd It is actually pretty tricky when you do dispatch on more than the first argument, especially if there are also hierarchies of classes. I still think there's a couple bugs related to this. As for when I do it with the signatures and when I "dispatch" manually, it is somewhat emergent :D I think but I'm not 100% sure that EIEIO matches the arguments in order, so if it ever specifies on the first argument it does not backtrack. Which might explain why your code didn't ever execute: it probably specified on something more specific than |
I think this is good to go, if you don't have any outstanding ideas we can go ahead and merge this. |
I should just have RTFM, because apparently multiple dispatch is not supported:
Sure, it's a good start! Looking forward to bang on the type system further, as I find the time. Thanks for taking the quick and thorough feedback. |
This seems highly improbable because at some places I rely on multiple dispatch. I'm so confused now. (look into the type helper.el file for the sum/diff combinators) |
Hey there! Nice little project. I have been dabbling with a type checker for Elisp myself, but it looks like it may be more useful to contribute to this one instead. I'll be overjoyed when it typechecks flycheck.el, so I can sleep more easily at night ;)
To get a feel for it, I've tackled #150.
In
foo.el
:cask exec elsa foo.el
:Note that the stray type error. To quell it, I think we need to modify
elsa--analyse:setq
and move the check for defconst assignment there. Currently I've added a rule to the 'variables' ruleset. Which way do you think is best?