@@ -1040,122 +1040,124 @@ defmodule Module.Types.Descr do
10401040 end
10411041 end
10421042 else
1043- normalized =
1044- case fun_normalize ( fun_static , arity , :static ) do
1045- { :ok , static_domain , static_arrows } when fun_dynamic == nil ->
1046- # TODO: Should dynamic_arrows be [] or static_arrows
1047- { :ok , static_domain , static_arrows , static_arrows }
1043+ with { :ok , domain , static_arrows , dynamic_arrows } <-
1044+ fun_normalize_both ( fun_static , fun_dynamic , arity ) do
1045+ cond do
1046+ not subtype? ( domain_descr ( arguments ) , domain ) ->
1047+ :badarg
10481048
1049- { :ok , static_domain , static_arrows } when fun_dynamic != nil ->
1050- case fun_normalize ( fun_dynamic , arity , :dynamic ) do
1051- { :ok , dynamic_domain , dynamic_arrows } ->
1052- domain = union ( dynamic_domain , dynamic ( union ( static_domain , dynamic_domain ) ) )
1053- { :ok , domain , static_arrows , dynamic_arrows }
1049+ static_arrows == [ ] ->
1050+ { :ok , dynamic ( fun_apply_static ( arguments , dynamic_arrows , false ) ) }
10541051
1055- _ ->
1056- # TODO: Should dynamic_arrows be [] or static_arrows
1057- { :ok , static_domain , static_arrows , static_arrows }
1058- end
1052+ true ->
1053+ # For dynamic cases, combine static and dynamic results
1054+ { static_args , dynamic_args , maybe_empty? } =
1055+ if args_dynamic? do
1056+ { materialize_arguments ( arguments , :up ) , materialize_arguments ( arguments , :down ) ,
1057+ true }
1058+ else
1059+ { arguments , arguments , false }
1060+ end
1061+
1062+ { :ok ,
1063+ union (
1064+ fun_apply_static ( static_args , static_arrows , false ) ,
1065+ dynamic ( fun_apply_static ( dynamic_args , dynamic_arrows , maybe_empty? ) )
1066+ ) }
1067+ end
1068+ end
1069+ end
1070+ end
10591071
1060- :badfun ->
1061- case fun_normalize ( fun_dynamic , arity , :dynamic ) do
1062- { :ok , dynamic_domain , dynamic_arrows } ->
1063- # TODO: Should static_arrows be [] or dynamic_arrows
1064- { :ok , dynamic_domain , [ ] , dynamic_arrows }
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 )
10651075
1066- error ->
1067- error
1068- end
1076+ defp are_arguments_dynamic? ( arguments ) , do: Enum . any? ( arguments , & match? ( % { dynamic: _ } , & 1 ) )
10691077
1070- error ->
1071- error
1078+ defp fun_normalize_both ( fun_static , fun_dynamic , arity ) do
1079+ case fun_normalize ( fun_static , arity , :static ) do
1080+ { :ok , static_domain , static_arrows } when fun_dynamic == nil ->
1081+ { :ok , static_domain , static_arrows , static_arrows }
1082+
1083+ { :ok , static_domain , static_arrows } when fun_dynamic != nil ->
1084+ case fun_normalize ( fun_dynamic , arity , :dynamic ) do
1085+ { :ok , dynamic_domain , dynamic_arrows } ->
1086+ domain = union ( dynamic_domain , dynamic ( static_domain ) )
1087+ { :ok , domain , static_arrows , dynamic_arrows }
1088+
1089+ _ ->
1090+ { :ok , static_domain , static_arrows , static_arrows }
10721091 end
10731092
1074- with { :ok , domain , static_arrows , dynamic_arrows } <- normalized do
1075- if subtype? ( domain_descr ( arguments ) , domain ) do
1076- # For dynamic cases, combine static and dynamic results
1077- { static_args , dynamic_args , maybe_empty? } =
1078- if args_dynamic? do
1079- { materialize_arguments ( arguments , :up ) , materialize_arguments ( arguments , :down ) ,
1080- true }
1081- else
1082- { arguments , arguments , false }
1083- end
1093+ :badfun ->
1094+ case fun_normalize ( fun_dynamic , arity , :dynamic ) do
1095+ { :ok , dynamic_domain , dynamic_arrows } ->
1096+ { :ok , dynamic_domain , [ ] , dynamic_arrows }
10841097
1085- { :ok ,
1086- union (
1087- fun_apply_static ( static_args , static_arrows , false ) ,
1088- dynamic ( fun_apply_static ( dynamic_args , dynamic_arrows , maybe_empty? ) )
1089- ) }
1090- else
1091- :badarg
1098+ error ->
1099+ error
10921100 end
1093- end
10941101
1095- # case fun_normalize(fun_static, arity, :static) do
1096- # {:ok, static_domain, static_arrow} when fun_dynamic == nil ->
1097- # with {:ok, res1} <-
1098- # new_fun_apply_static(static_domain, static_arrows, static_args, false),
1099- # {:ok, res2} <-
1100- # new_fun_apply_static(static_domain, static_arrows, dynamic_args, maybe_empty?) do
1101- # {:ok, union(res1, dynamic(res2))}
1102- # end
1103-
1104- # {:ok, static_domain, static_arrows} when fun_dynamic != nil ->
1105- # case fun_normalize(fun_dynamic, arity, :dynamic) do
1106- # {:ok, dynamic_domain, dynamic_arrows} ->
1107- # with {:ok, res1} <-
1108- # checked_fun_apply_static(
1109- # union(dynamic_domain, static_domain),
1110- # static_domain,
1111- # static_arrows,
1112- # static_args,
1113- # false
1114- # ) do
1115- # case checked_fun_apply_static(
1116- # dynamic_domain,
1117- # dynamic_domain,
1118- # dynamic_arrows,
1119- # dynamic_args,
1120- # maybe_empty?
1121- # ) do
1122- # {:ok, res2} ->
1123- # {:ok, union(res1, dynamic(res2))}
1124-
1125- # _ ->
1126- # {:ok, res1}
1127- # end
1128- # end
1129-
1130- # _ ->
1131- # new_fun_apply_static(static_domain, static_arrows, static_args, false)
1132- # end
1133-
1134- # :badfun ->
1135- # # Then the dynamic call has to succeed
1136- # result =
1137- # if fun_dynamic do
1138- # fun_apply_static(fun_dynamic, dynamic_args, :dynamic, maybe_empty?)
1139- # else
1140- # fun_apply_static(fun_static, dynamic_args, :static, maybe_empty?)
1141- # end
1142-
1143- # with {:ok, descr} <- result do
1144- # {:ok, dynamic(descr)}
1145- # end
1146-
1147- # # badarg/badarity
1148- # error ->
1149- # error
1150- # end
1102+ error ->
1103+ error
11511104 end
11521105 end
11531106
1154- # Materializes arguments using the specified direction (up or down)
1155- defp materialize_arguments ( arguments , :up ) , do: Enum . map ( arguments , & upper_bound / 1 )
1156- defp materialize_arguments ( arguments , :down ) , do: Enum . map ( arguments , & lower_bound / 1 )
1107+ # Transforms a binary decision diagram (BDD) into the canonical form {domain, arrows, arity}:
1108+ #
1109+ # 1. **domain**: The union of all domains from positive functions in the BDD
1110+ # 2. **arrows**: List of lists, where each inner list contains an intersection of function arrows
1111+ # 3. **arity**: Function arity (number of parameters)
1112+ #
1113+ ## Return Values
1114+ #
1115+ # - `{domain, arrows, arity}` for valid function BDDs
1116+ # - `:badfun` if the BDD represents an empty function type
1117+ #
1118+ # ## Internal Use
1119+ #
1120+ # This function is used internally by `fun_apply`, and others to
1121+ # ensure consistent handling of function types in all operations.
1122+ defp fun_normalize ( % { fun: bdd } , arity , mode ) do
1123+ { domain , arrows , bad_arities } =
1124+ Enum . reduce ( fun_get ( bdd ) , { term ( ) , [ ] , [ ] } , fn
1125+ { [ { args , _ } | _ ] = pos_funs , neg_funs } , { domain , arrows , bad_arities } ->
1126+ arrow_arity = length ( args )
11571127
1158- defp are_arguments_dynamic? ( arguments ) , do: Enum . any? ( arguments , & match? ( % { dynamic: _ } , & 1 ) )
1128+ cond do
1129+ arrow_arity != arity ->
1130+ { domain , arrows , [ arrow_arity | bad_arities ] }
1131+
1132+ fun_empty? ( pos_funs , neg_funs ) ->
1133+ { domain , arrows , bad_arities }
1134+
1135+ true ->
1136+ # Calculate domain from all positive functions
1137+ path_domain =
1138+ Enum . reduce ( pos_funs , none ( ) , fn { args , _ } , acc ->
1139+ union ( acc , domain_descr ( args ) )
1140+ end )
1141+
1142+ { intersection ( domain , path_domain ) , [ pos_funs | arrows ] , bad_arities }
1143+ end
1144+ end )
1145+
1146+ case { arrows , bad_arities } do
1147+ { [ ] , [ ] } ->
1148+ :badfun
1149+
1150+ { arrows , [ _ | _ ] = bad_arities } when mode == :static or arrows == [ ] ->
1151+ { :badarity , Enum . uniq ( bad_arities ) }
1152+
1153+ { _ , _ } ->
1154+ { :ok , domain , arrows }
1155+ end
1156+ end
1157+
1158+ defp fun_normalize ( % { } , _arity , _mode ) do
1159+ :badfun
1160+ end
11591161
11601162 defp fun_apply_static ( arguments , arrows , maybe_empty? ) do
11611163 type_args = domain_descr ( arguments )
@@ -1179,55 +1181,6 @@ defmodule Module.Types.Descr do
11791181 end
11801182 end
11811183
1182- # defp fun_apply_static(descr, arguments, mode, maybe_empty?) do
1183- # arity = length(arguments)
1184-
1185- # with {:ok, domain, arrows} <- fun_normalize(descr, arity, mode) do
1186- # new_fun_apply_static(domain, arrows, arguments, maybe_empty?)
1187- # end
1188- # end
1189-
1190- # defp checked_fun_apply_static(check_domain, domain, arrows, arguments, maybe_empty?) do
1191- # if subtype?(domain_descr(arguments), check_domain) do
1192- # new_fun_apply_static(domain, arrows, arguments, maybe_empty?)
1193- # else
1194- # :badarg
1195- # end
1196- # end
1197-
1198- # defp new_fun_apply_static(domain, arrows, arguments, maybe_empty?) do
1199- # type_args = domain_descr(arguments)
1200-
1201- # cond do
1202- # # Optimization: short-circuits when inner loop is none() or outer loop is term()
1203- # maybe_empty? and empty?(type_args) ->
1204- # result =
1205- # Enum.reduce_while(arrows, none(), fn intersection_of_arrows, acc ->
1206- # Enum.reduce_while(intersection_of_arrows, term(), fn
1207- # {_dom, _ret}, acc when acc == @none -> {:halt, acc}
1208- # {_dom, ret}, acc -> {:cont, intersection(acc, ret)}
1209- # end)
1210- # |> case do
1211- # :term -> {:halt, :term}
1212- # inner -> {:cont, union(inner, acc)}
1213- # end
1214- # end)
1215-
1216- # {:ok, result}
1217-
1218- # subtype?(type_args, domain) ->
1219- # result =
1220- # Enum.reduce(arrows, none(), fn intersection_of_arrows, acc ->
1221- # aux_apply(acc, type_args, term(), intersection_of_arrows)
1222- # end)
1223-
1224- # {:ok, result}
1225-
1226- # true ->
1227- # :badarg
1228- # end
1229- # end
1230-
12311184 # Helper function for function application that handles the application of
12321185 # function arrows to input types.
12331186
@@ -1290,61 +1243,6 @@ defmodule Module.Types.Descr do
12901243 end
12911244 end
12921245
1293- # Transforms a binary decision diagram (BDD) into the canonical form {domain, arrows, arity}:
1294- #
1295- # 1. **domain**: The union of all domains from positive functions in the BDD
1296- # 2. **arrows**: List of lists, where each inner list contains an intersection of function arrows
1297- # 3. **arity**: Function arity (number of parameters)
1298- #
1299- ## Return Values
1300- #
1301- # - `{domain, arrows, arity}` for valid function BDDs
1302- # - `:badfun` if the BDD represents an empty function type
1303- #
1304- # ## Internal Use
1305- #
1306- # This function is used internally by `fun_apply`, and others to
1307- # ensure consistent handling of function types in all operations.
1308- defp fun_normalize ( % { fun: bdd } , arity , mode ) do
1309- { domain , arrows , bad_arities } =
1310- Enum . reduce ( fun_get ( bdd ) , { term ( ) , [ ] , [ ] } , fn
1311- { [ { args , _ } | _ ] = pos_funs , neg_funs } , { domain , arrows , bad_arities } ->
1312- arrow_arity = length ( args )
1313-
1314- cond do
1315- arrow_arity != arity ->
1316- { domain , arrows , [ arrow_arity | bad_arities ] }
1317-
1318- fun_empty? ( pos_funs , neg_funs ) ->
1319- { domain , arrows , bad_arities }
1320-
1321- true ->
1322- # Calculate domain from all positive functions
1323- path_domain =
1324- Enum . reduce ( pos_funs , none ( ) , fn { args , _ } , acc ->
1325- union ( acc , domain_descr ( args ) )
1326- end )
1327-
1328- { intersection ( domain , path_domain ) , [ pos_funs | arrows ] , bad_arities }
1329- end
1330- end )
1331-
1332- case { arrows , bad_arities } do
1333- { [ ] , [ ] } ->
1334- :badfun
1335-
1336- { arrows , [ _ | _ ] = bad_arities } when mode == :static or arrows == [ ] ->
1337- { :badarity , Enum . uniq ( bad_arities ) }
1338-
1339- { _ , _ } ->
1340- { :ok , domain , arrows }
1341- end
1342- end
1343-
1344- defp fun_normalize ( % { } , _arity , _mode ) do
1345- :badfun
1346- end
1347-
13481246 # Checks if a function type is empty.
13491247 #
13501248 # A function type is empty if:
0 commit comments