-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support for rigid type variables (#560)
Until this commit, we had no notion of rigid type variables in Gradualizer. All type variables were treated as flexible and thus errors in polymorphic functions were not discovered. Even obvious errors like -spec id(A) -> A. id(_X) -> banana. were left unreported. This commit adds a new syntactic form of `type()`: `{rigid_var, anno(), atom()}`. All type variables mentioned in a spec are convert to rigid_vars during typechecking of the particular function. Rigid type variables have no subtypes or supertypes (except for itself, top, none(), and any()), and therefore we treat them as types we know nothing about (as they can be anything; their type determines the caller, not the callee). I've also added some missing cases to `subst_ty/2`. * Move the poly_2 test functions to should_fail All these function coming from the paper "Bidirectional Typing for Erlang" (N. Rajendrakumar, A. Bieniusa, 2021) require higher-rank (at least rank-2) polymorphism. To be able to have higher-ranked polymorphism, we would have to be able to express inner quantifiers. We don't have that in the typespec syntax, so the traditional way of interpreting type variables in specs is to treat them all as quantified at the top-level. Authors of the paper took a non-standard approach, to quote it: Type specifications in Erlang consider all type variables as universal and thus restrict the types to prefix or rank-1 polymorphism. For allowing higher-rank polymorphism, we are interpreting the type specification differently, namely we are adding the quantifier to the type variable with the smallest scope. -spec poly_2(fun((A) -> A)) -> {boolean(), integer()}. For example, the type spec for function poly_2 above is interpreted as (∀a.(a) → a) → {boolean(), integer()} instead of ∀a.((a → a) → {boolean(), integer()}) I argue that this interpretation of type variables would be confusing for users. Additionally, I think that there is not much value in having higher-rank polymorphism in a dynamically/gradually typed language. In Haskell, it is useful for sure (even though one does not encounter it very often) but this is because there is a strict typechecker that doesn't accept anything you cannot type well. Here, in Erlang, if you ever come to a situation where you need higher-rank polymorphism (and I think it is quite unlikely), you can always fallback to any().
- Loading branch information
Showing
14 changed files
with
254 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 0 additions & 6 deletions
6
test/known_problems/should_fail/rigid_type_variables_fail.erl
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,21 @@ | ||
-module(opaque_fail). | ||
|
||
-export([use_external/2]). | ||
-export([use_external/2, update_without_opaque/0, add_to_opaque/0, return_opaque/0]). | ||
|
||
-spec use_external(user_types:my_opaque(), integer() | undefined) -> integer(). | ||
use_external(I, undefined) -> I; | ||
use_external(_, I) -> I. | ||
|
||
-spec update_without_opaque() -> ok. | ||
update_without_opaque() -> | ||
_Val = user_types:update_opaque(3), | ||
ok. | ||
|
||
-spec add_to_opaque() -> ok. | ||
add_to_opaque() -> | ||
Val = user_types:new_opaque(), | ||
Val + 1, | ||
ok. | ||
|
||
-spec return_opaque() -> user_types:my_opaque(). | ||
return_opaque() -> 3. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.