1
- # Gradual and strict typing modes
1
+ # Gradual typing
2
2
3
- EqWAlizer features two different modes: gradual mode, and strict mode. They differ in how
4
- unannotated functions are handled, and how the builtin constant ` eqwalizer:dynamic() ` behaves.
5
- Strict mode offer stronger type-checking guarantees, while gradual mode favours ease-of-use
6
- and better signal-to-noise.
7
- Strict mode is kept sound, however, current development of eqWAlizer focuses on optimising for
8
- gradual mode. The current distribution of eqWAlizer uses gradual mode by default.
3
+ EqWAlizer features gradual typing, a typing discipline built on type ` dynamic() ` ,
4
+ that allows smooth migration of untyped code to typed code, and precise management
5
+ of the signal-to-noise ratio of type errors.
9
6
10
7
11
- ### Type ` eqwalizer: dynamic()`
8
+ ### Type ` dynamic() `
12
9
13
- ` eqwalizer:dynamic() ` (later abbreviated to ` dynamic() ` ) is a special type which,
14
- in gradual mode, "slips through the fingers" of the type-checker. It is similar to
10
+ Erlang's built-in type ` dynamic() ` is considered by eqWAlizer as a special type which
11
+ "slips through the fingers" of the type-checker. It is similar to
15
12
[ any in TypeScript] ( https://www.typescriptlang.org/docs/handbook/basic-types.html#any ) ,
16
13
[ dynamic in Hack] ( https://docs.hhvm.com/hack/built-in-types/dynamic ) ,
17
14
[ untyped in Sorbet] ( https://sorbet.org/docs/untyped ) .
18
- Formally, in gradual mode, ` dynamic() ` is compatible with every type (and, conversely,
15
+ Formally, ` dynamic() ` is compatible with every type (and, conversely,
19
16
every type is compatible with ` dynamic() ` ). This
20
17
means that a function that expects an argument of type ` dynamic() ` can be used with any
21
18
value, and a result of type ` dynamic() ` can be used anywhere.
@@ -27,35 +24,23 @@ This value can in particular be passed to a function expecting, say, an argument
27
24
` {integer(), boolean()} ` , but not to a function expecting an argument of type
28
25
` {integer(), boolean(), atom()} ` .
29
26
30
- In strict mode, eqWAlizer considers ` dynamic() ` to be equivalent to ` term() ` . This is a
31
- completely different, stricter, behaviour. In strict mode, a function that accepts arguments
32
- of type ` dynamic() ` is considered by eqWAlizer as accepting arguments of type ` term() ` . Hence,
33
- this function can be applied to values of any type, which is basically the same behaviour as in
34
- gradual mode. However, in strict mode, a value of type ` dynamic() ` ** cannot** be used in every
35
- context. It can only be used in contexts expecting a value of type ` term() ` , since
36
- the only supertype of ` dynamic() ` in strict mode is ` term() ` . As an example, consider the
37
- following function:
27
+ As an example, consider the following function:
38
28
``` Erlang
39
- -spec dyn_to_int (eqwalizer : dynamic ()) -> integer ().
29
+ -spec dyn_to_int (dynamic ()) -> integer ().
40
30
dyn_to_int (X ) -> X .
41
31
```
42
- This function is accepted by eqWAlizer in gradual mode: ` X ` is of type ` eqwalizer:dynamic() `
43
- which, in gradual mode, is compatible with every type, in particular ` integer() ` . However, in
44
- strict mode, ` X ` is considered to be of type ` term() ` which is not compatible with ` integer() ` .
32
+ This function is accepted by eqWAlizer: ` X ` is of type ` dynamic() `
33
+ which is compatible with every type, in particular ` integer() ` .
45
34
46
- The type ` eqwalizer:dynamic() ` is provided in the module ` eqwalizer.erl ` , which should be
47
- made available during analysis. This module is present in the open-source library
48
- [ eqwalizer_support] ( https://github.com/WhatsApp/eqwalizer/tree/main/eqwalizer_support ) .
49
35
50
-
51
- ### Using ` eqwalizer:dynamic() ` in gradual mode
36
+ ### Using ` dynamic() `
52
37
53
38
The purpose of ` dynamic() ` is twofold:
54
39
55
40
- provide signal for unspecced functions;
56
41
- handle mixtures of specced and unspecced code gracefully without much noise.
57
42
58
- While it is possible to manually introduce the constant ` eqwalizer: dynamic()` , the dynamic
43
+ While it is possible to manually introduce the constant ` dynamic() ` , the dynamic
59
44
type is introduced automatically by eqWAlizer in gradual mode in certain cases:
60
45
61
46
- as parameter types and result types of unspecced functions, both when type-checking their
@@ -124,10 +109,8 @@ truly be well-typed.
124
109
125
110
#### Example 2: less noise for invocations of unspecced functions
126
111
127
- Consider an unspecced function ` unspecced/1 ` . When invoking it and running eqWAlizer in
128
- strict mode, it would simply report that it does not know the spec of ` unspecced/1 ` and
129
- give up. In gradual mode, eqWAlizer instead supposes that ` unspecced/1 ` actually has the
130
- spec ` dynamic() -> dynamic() ` and uses this to report some actual
112
+ Consider an unspecced function ` unspecced/1 ` . To provide some signal, eqWAlizer supposes that
113
+ ` unspecced/1 ` actually has the spec ` dynamic() -> dynamic() ` and uses this to report some actual
131
114
type errors:
132
115
``` Erlang
133
116
-spec call_unspecced () -> binary ().
0 commit comments