@@ -96,7 +96,7 @@ defmodule Module.Types.Descr do
9696 iex> fun([integer()], atom()) # Creates (integer) -> atom
9797 iex> fun([integer(), float()], boolean()) # Creates (integer, float) -> boolean
9898 """
99- def fun ( args , return ) when is_list ( args ) , do: % { fun: fun_descr ( args , return ) }
99+ def fun ( args , return ) when is_list ( args ) , do: fun_descr ( args , return )
100100
101101 @ doc """
102102 Creates the top function type for the given arity, where all arguments are none()
@@ -893,7 +893,9 @@ defmodule Module.Types.Descr do
893893
894894 # * Representation:
895895 # - fun(): Top function type (leaf 1)
896- # - Function literals: {[t1, ..., tn], t} where [t1, ..., tn] are argument types and t is return type
896+ # - Function literals: {tag, [t1, ..., tn], t} where [t1, ..., tn] are argument types and t is return type
897+ # tag is either `:weak` or `:strong`
898+ # TODO: implement `:strong`
897899 # - Normalized form for function applications: {domain, arrows, arity} is produced by `fun_normalize/1`
898900
899901 # * Examples:
@@ -904,12 +906,7 @@ defmodule Module.Types.Descr do
904906 # unary functions with tuple domains to handle special cases like representing functions of a
905907 # specific arity (e.g., (none,none->term) for arity 2).
906908
907- defp fun_descr ( inputs , output ) , do: { { :weak , inputs , output } , 1 , 0 }
908-
909- @ doc "Utility function: fast build an intersection from a list of functions"
910- def fun_from_intersection ( intersection ) do
911- Enum . reduce ( intersection , 1 , fn { dom , ret } , acc -> { { :weak , dom , ret } , acc , 0 } end )
912- end
909+ defp fun_new ( inputs , output ) , do: { { :weak , inputs , output } , 1 , 0 }
913910
914911 @ doc """
915912 Creates a function type from a list of inputs and an output where the inputs and/or output may be dynamic.
@@ -919,34 +916,28 @@ defmodule Module.Types.Descr do
919916 - Dynamic part: dynamic(down(t) → up(s))
920917
921918 When handling dynamic types:
922- - `up(t)` extracts the upper bound (most general type) of a gradual type
923- - `down(t)` extracts the lower bound (most specific type) of a gradual type
919+ - `up(t)` extracts the upper bound (most general type) of a gradual type.
920+ For `dynamic(integer())`, it is `integer()`.
921+ - `down(t)` extracts the lower bound (most specific type) of a gradual type.
924922 """
925- def fun_from_annotation ( inputs , output ) do
926- dynamic_arguments? = are_arguments_dynamic? ( inputs )
923+ def fun_descr ( args , output ) when is_list ( args ) do
924+ dynamic_arguments? = are_arguments_dynamic? ( args )
927925 dynamic_output? = match? ( % { dynamic: _ } , output )
928926
929- cond do
930- dynamic_arguments? and dynamic_output? ->
931- static_part = fun ( materialize_arguments ( inputs , :up ) , down ( output ) )
932- dynamic_part = dynamic ( fun ( materialize_arguments ( inputs , :down ) , up ( output ) ) )
933- union ( static_part , dynamic_part )
934-
935- # Only arguments are dynamic
936- dynamic_arguments? ->
937- static_part = fun ( materialize_arguments ( inputs , :up ) , output )
938- dynamic_part = dynamic ( fun ( materialize_arguments ( inputs , :down ) , output ) )
939- union ( static_part , dynamic_part )
940-
941- # Only return type is dynamic
942- dynamic_output? ->
943- static_part = fun ( inputs , down ( output ) )
944- dynamic_part = dynamic ( fun ( inputs , up ( output ) ) )
945- union ( static_part , dynamic_part )
927+ if dynamic_arguments? or dynamic_output? do
928+ input_static = if dynamic_arguments? , do: materialize_arguments ( args , :up ) , else: args
929+ input_dynamic = if dynamic_arguments? , do: materialize_arguments ( args , :down ) , else: args
946930
947- true ->
948- # No dynamic components, use standard function type
949- fun ( inputs , output )
931+ output_static = if dynamic_output? , do: down ( output ) , else: output
932+ output_dynamic = if dynamic_output? , do: up ( output ) , else: output
933+
934+ % {
935+ fun: fun_new ( input_static , output_static ) ,
936+ dynamic: % { fun: fun_new ( input_dynamic , output_dynamic ) }
937+ }
938+ else
939+ # No dynamic components, use standard function type
940+ % { fun: fun_new ( args , output ) }
950941 end
951942 end
952943
@@ -962,7 +953,7 @@ defmodule Module.Types.Descr do
962953 # Example: for functions (integer,float)->:ok and (float,integer)->:error
963954 # domain isn't {integer|float,integer|float} as that would incorrectly accept {float,float}
964955 # Instead, it is {integer,float} or {float,integer}
965- def domain_new ( types ) when is_list ( types ) , do: tuple ( types )
956+ def domain_descr ( types ) when is_list ( types ) , do: tuple ( types )
966957
967958 @ doc """
968959 Calculates the domain of a function type.
@@ -1063,9 +1054,13 @@ defmodule Module.Types.Descr do
10631054 atom()
10641055 """
10651056 def fun_apply ( fun , arguments ) do
1066- case :maps . take ( :dynamic , fun ) do
1067- :error -> fun_apply_with_strategy ( fun , nil , arguments )
1068- { fun_dynamic , fun_static } -> fun_apply_with_strategy ( fun_static , fun_dynamic , arguments )
1057+ if empty? ( domain_descr ( arguments ) ) do
1058+ :badarguments
1059+ else
1060+ case :maps . take ( :dynamic , fun ) do
1061+ :error -> fun_apply_with_strategy ( fun , nil , arguments )
1062+ { fun_dynamic , fun_static } -> fun_apply_with_strategy ( fun_static , fun_dynamic , arguments )
1063+ end
10691064 end
10701065 end
10711066
@@ -1100,7 +1095,7 @@ defmodule Module.Types.Descr do
11001095 defp are_arguments_dynamic? ( arguments ) , do: Enum . any? ( arguments , & match? ( % { dynamic: _ } , & 1 ) )
11011096
11021097 defp fun_apply_static ( % { fun: fun_bdd } , arguments ) do
1103- type_args = domain_new ( arguments )
1098+ type_args = domain_descr ( arguments )
11041099
11051100 if empty? ( type_args ) do
11061101 # At this stage we do not check that the function can be applied to the arguments (using domain)
@@ -1129,9 +1124,7 @@ defmodule Module.Types.Descr do
11291124
11301125 { :ok , result }
11311126 else
1132- :emptyfunction -> :emptyfunction
1133- :badarguments -> :badarguments
1134- false -> :badarguments
1127+ _ -> :badarguments
11351128 end
11361129 end
11371130 end
@@ -1156,7 +1149,7 @@ defmodule Module.Types.Descr do
11561149
11571150 defp aux_apply ( result , input , returns_reached , [ { _tag , dom , ret } | arrow_intersections ] ) do
11581151 # Calculate the part of the input not covered by this arrow's domain
1159- dom_subtract = difference ( input , domain_new ( dom ) )
1152+ dom_subtract = difference ( input , domain_descr ( dom ) )
11601153
11611154 # Refine the return type by intersecting with this arrow's return type
11621155 ret_refine = intersection ( returns_reached , ret )
@@ -1229,7 +1222,9 @@ defmodule Module.Types.Descr do
12291222
12301223 # Calculate domain from all positive functions
12311224 path_domain =
1232- Enum . reduce ( pos_funs , none ( ) , fn { _ , args , _ } , acc -> union ( acc , domain_new ( args ) ) end )
1225+ Enum . reduce ( pos_funs , none ( ) , fn { _ , args , _ } , acc ->
1226+ union ( acc , domain_descr ( args ) )
1227+ end )
12331228
12341229 { intersection ( domain , path_domain ) , [ pos_funs | arrows ] , new_arity }
12351230 end
@@ -1269,6 +1264,8 @@ defmodule Module.Types.Descr do
12691264 # - `{[fun(1), fun(2)], []}` is empty (different arities)
12701265 # - `{[fun(integer() -> atom())], [fun(none() -> term())]}` is empty
12711266 # - `{[], _}` (representing the top function type fun()) is never empty
1267+ #
1268+ # TODO: test performance
12721269 defp fun_empty? ( [ ] , _ ) , do: false
12731270
12741271 defp fun_empty? ( positives , negatives ) do
@@ -1287,7 +1284,7 @@ defmodule Module.Types.Descr do
12871284 # function's domain is a supertype of the positive domain and if the phi function
12881285 # determines emptiness.
12891286 length ( neg_arguments ) == positive_arity and
1290- subtype? ( domain_new ( neg_arguments ) , positive_domain ) and
1287+ subtype? ( domain_descr ( neg_arguments ) , positive_domain ) and
12911288 phi_starter ( neg_arguments , negation ( neg_return ) , positives )
12921289 end )
12931290 end
@@ -1300,10 +1297,10 @@ defmodule Module.Types.Descr do
13001297 positives
13011298 |> Enum . reduce_while ( { :empty , none ( ) } , fn
13021299 { _tag , args , _ } , { :empty , _ } ->
1303- { :cont , { length ( args ) , domain_new ( args ) } }
1300+ { :cont , { length ( args ) , domain_descr ( args ) } }
13041301
13051302 { _tag , args , _ } , { arity , dom } when length ( args ) == arity ->
1306- { :cont , { arity , union ( dom , domain_new ( args ) ) } }
1303+ { :cont , { arity , union ( dom , domain_descr ( args ) ) } }
13071304
13081305 { _tag , _args , _ } , { _arity , _ } ->
13091306 { :halt , { :empty , none ( ) } }
@@ -2986,10 +2983,10 @@ defmodule Module.Types.Descr do
29862983
29872984 ## Examples
29882985
2989- iex> tuple_fetch(domain_new ([integer(), atom()]), 0)
2986+ iex> tuple_fetch(domain_descr ([integer(), atom()]), 0)
29902987 {false, integer()}
29912988
2992- iex> tuple_fetch(union(domain_new ([integer()]), domain_new ([integer(), atom()])), 1)
2989+ iex> tuple_fetch(union(domain_descr ([integer()]), domain_descr ([integer(), atom()])), 1)
29932990 {true, atom()}
29942991
29952992 iex> tuple_fetch(dynamic(), 0)
0 commit comments