@@ -930,31 +930,19 @@ defmodule Module.Types.Descr do
930930 # Creates a function type from a list of inputs and an output
931931 # where the inputs and/or output may be dynamic.
932932 #
933- # For function (t → s) with dynamic components:
933+ # One approach is, for function (t → s) with dynamic components:
934934 # - Static part: (upper_bound(t) → lower_bound(s))
935935 # - Dynamic part: dynamic(lower_bound(t) → upper_bound(s))
936936 #
937- # When handling dynamic types:
938- # - `upper_bound(t)` extracts the upper bound (most general type) of a gradual type.
939- # For `dynamic(integer())`, it is `integer( )`.
940- # - `lower_bound(t)` extracts the lower bound (most specific type) of a gradual type .
937+ # However, this comes with the downside that ` dynamic(integer()) -> binary()`
938+ # cannot receive integers as arguments. So instead we surface the dynamic up,
939+ # as we do for other data types, converting it to `dynamic(( integer() -> binary()) )`.
940+ # One could obtain the other type if desired by explicitly defining it .
941941 defp fun_descr ( args , output ) when is_list ( args ) do
942- dynamic_arguments? = are_arguments_dynamic? ( args )
943- dynamic_output? = match? ( % { dynamic: _ } , output )
944-
945- if dynamic_arguments? or dynamic_output? do
946- input_static = if dynamic_arguments? , do: materialize_arguments ( args , :up ) , else: args
947- input_dynamic = if dynamic_arguments? , do: materialize_arguments ( args , :down ) , else: args
948-
949- output_static = if dynamic_output? , do: lower_bound ( output ) , else: output
950- output_dynamic = if dynamic_output? , do: upper_bound ( output ) , else: output
951-
952- % {
953- fun: fun_new ( input_static , output_static ) ,
954- dynamic: % { fun: fun_new ( input_dynamic , output_dynamic ) }
955- }
942+ if any_dynamic? ( [ output | args ] ) do
943+ [ output | args ] = Enum . map ( [ output | args ] , & upper_bound / 1 )
944+ % { dynamic: % { fun: fun_new ( args , output ) } }
956945 else
957- # No dynamic components, use standard function type
958946 % { fun: fun_new ( args , output ) }
959947 end
960948 end
@@ -1027,7 +1015,7 @@ defmodule Module.Types.Descr do
10271015 defp fun_only? ( descr ) , do: empty? ( Map . delete ( descr , :fun ) )
10281016
10291017 defp fun_apply_with_strategy ( fun_static , fun_dynamic , arguments ) do
1030- args_dynamic? = are_arguments_dynamic ?( arguments )
1018+ args_dynamic? = any_dynamic ?( arguments )
10311019 arity = length ( arguments )
10321020
10331021 # For non-dynamic function and arguments, just return the static result
@@ -1053,8 +1041,7 @@ defmodule Module.Types.Descr do
10531041 # For dynamic cases, combine static and dynamic results
10541042 { static_args , dynamic_args , maybe_empty? } =
10551043 if args_dynamic? do
1056- { materialize_arguments ( arguments , :up ) , materialize_arguments ( arguments , :down ) ,
1057- true }
1044+ { Enum . map ( arguments , & upper_bound / 1 ) , Enum . map ( arguments , & lower_bound / 1 ) , true }
10581045 else
10591046 { arguments , arguments , false }
10601047 end
@@ -1069,11 +1056,7 @@ defmodule Module.Types.Descr do
10691056 end
10701057 end
10711058
1072- # Materializes arguments using the specified direction (up or down)
1073- defp materialize_arguments ( arguments , :up ) , do: Enum . map ( arguments , & upper_bound / 1 )
1074- defp materialize_arguments ( arguments , :down ) , do: Enum . map ( arguments , & lower_bound / 1 )
1075-
1076- defp are_arguments_dynamic? ( arguments ) , do: Enum . any? ( arguments , & match? ( % { dynamic: _ } , & 1 ) )
1059+ defp any_dynamic? ( arguments ) , do: Enum . any? ( arguments , & match? ( % { dynamic: _ } , & 1 ) )
10771060
10781061 defp fun_normalize_both ( fun_static , fun_dynamic , arity ) do
10791062 case fun_normalize ( fun_static , arity , :static ) do
0 commit comments