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

Add support for module_info functions #496

Merged
merged 1 commit into from
Dec 16, 2022
Merged

Conversation

xxdavid
Copy link
Collaborator

@xxdavid xxdavid commented Dec 6, 2022

I finally come up with a PR that fixes #473.

I am an Elixir person and haven't written almost any Erlang code before so there may be some things that could be done more idiomatically.

I think I got some (quite little still) idea how Gradualizer works but one thing still isn't clear to me – why isn't type_check_expr_in implemented in terms of type_check_expr. From my limited perspective, it could just determine the type of the expression by calling type_check_expr_in and then check whether the type is indeed a subtype of ResTy. Why wouldn't that work?

@erszcz
Copy link
Collaborator

erszcz commented Dec 6, 2022

Hi, @xxdavid! This is great!

I'll currently leave the review for a bit later, but I think I can share something on the question you ask:

why isn't type_check_expr_in implemented in terms of type_check_expr

In a nutshell, type inference is a significantly harder problem than type checking. In general, it might be possible to type check a fully annotated program in a hypothetical language in a reasonable time, but not to rebuild the annotations if they're missing. Actually, full type inference of practical languages like Haskell or OCaml is undecidable - that's why in some cases the programmer has to provide type annotations to enable a program to compile.

It's not a new problem, though, which has focused interest on local methods originally known as local type inference and more recently as bidirectional typing. In practice, it means a type checker alternates between inferencing types where it can and only checking types where it cannot do any better (these are the places where we need annotations to be provided by a programmer). A great paper on that topic is Bidirectional Typing by Dunfield and Krishnaswami.

BTW, this question was previously raised, possibly even multiple times, and possibly by me the last time :D @josefs replied back then that:

It would be great if it was possible to merge the two functions in a way that also made the code simpler. But I don't know how to do that.

Recently I also noticed some repetition between these functions, so maybe in practice there's room for improvement. In general, though, type_check_expr_in (type checking) is easier, whereas type_check_expr (synthesizing aka inferencing a type) might not be possible at all for some expressions. I haven't compared them in detail to tell if there are possible simplifications to be made.

@xxdavid
Copy link
Collaborator Author

xxdavid commented Dec 6, 2022

Aha! I haven't thought of that when raising the question. Thanks for your explanation! :slight_smile: And also for linking the previous issue you created that provides additional context. I'm glad I can associate the mysterious _in with a word (inward). 😄 I noticed that you sprinkled the Bidirectional typing paper also in our earlier discussion but I still haven't read it, I look forward forward to reading it after I grasp some of the more basic stuff.

@erszcz
Copy link
Collaborator

erszcz commented Dec 16, 2022

This looks great, thanks @xxdavid! Congrats on the first contribution 🎉

@erszcz erszcz merged commit 82a0e7d into josefs:master Dec 16, 2022
@erszcz
Copy link
Collaborator

erszcz commented Dec 17, 2022

@xxdavid Ahh, of course I missed something in the review 😅 Fixed in #498. It's worth taking a look at https://github.com/josefs/Gradualizer/actions/runs/3715667032/jobs/6301095725#step:6:14 even if CI is green. The current baseline on master is 39 lines of output.

Comment on lines +1661 to +1663
do_type_check_expr(Env, {call, P, {remote, _, _Mod, {atom, _, module_info}} = Name, Args}) ->
Arity = arity(length(Args)),
FunTy = get_module_info_type(Arity),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose we have a problem if Arity > 1. Perhaps we should break this clause up into explicit matching on arity 0 (Args = []) and 1 (Args = [_]) and fall though for any other arity?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in #500.

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

Successfully merging this pull request may close these issues.

Undefined function module_info/0
3 participants