Skip to content

Commit 9249513

Browse files
committed
Revert "Surface dynamic up in function creation"
This reverts commit f253a73.
1 parent ca7ef42 commit 9249513

File tree

2 files changed

+46
-12
lines changed

2 files changed

+46
-12
lines changed

lib/elixir/lib/module/types/descr.ex

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -944,19 +944,31 @@ defmodule Module.Types.Descr do
944944
# Creates a function type from a list of inputs and an output
945945
# where the inputs and/or output may be dynamic.
946946
#
947-
# One approach is, for function (t → s) with dynamic components:
947+
# For function (t → s) with dynamic components:
948948
# - Static part: (upper_bound(t) → lower_bound(s))
949949
# - Dynamic part: dynamic(lower_bound(t) → upper_bound(s))
950950
#
951-
# However, this comes with the downside that `dynamic(integer()) -> binary()`
952-
# cannot receive integers as arguments. So instead we surface the dynamic up,
953-
# as we do for other data types, converting it to `dynamic((integer() -> binary()))`.
954-
# One could obtain the other type if desired by explicitly defining it.
951+
# When handling dynamic types:
952+
# - `upper_bound(t)` extracts the upper bound (most general type) of a gradual type.
953+
# For `dynamic(integer())`, it is `integer()`.
954+
# - `lower_bound(t)` extracts the lower bound (most specific type) of a gradual type.
955955
defp fun_descr(args, output) when is_list(args) do
956-
if any_dynamic?([output | args]) do
957-
[output | args] = Enum.map([output | args], &upper_bound/1)
958-
%{dynamic: %{fun: fun_new(args, output)}}
956+
dynamic_arguments? = any_dynamic?(args)
957+
dynamic_output? = match?(%{dynamic: _}, output)
958+
959+
if dynamic_arguments? or dynamic_output? do
960+
input_static = if dynamic_arguments?, do: Enum.map(args, &upper_bound/1), else: args
961+
input_dynamic = if dynamic_arguments?, do: Enum.map(args, &lower_bound/1), else: args
962+
963+
output_static = if dynamic_output?, do: lower_bound(output), else: output
964+
output_dynamic = if dynamic_output?, do: upper_bound(output), else: output
965+
966+
%{
967+
fun: fun_new(input_static, output_static),
968+
dynamic: %{fun: fun_new(input_dynamic, output_dynamic)}
969+
}
959970
else
971+
# No dynamic components, use standard function type
960972
%{fun: fun_new(args, output)}
961973
end
962974
end

lib/elixir/test/elixir/module/types/descr_test.exs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ defmodule Module.Types.DescrTest do
777777
assert fun_apply(fun1, [integer()]) == {:ok, atom()}
778778
assert fun_apply(fun1, [float()]) == {:ok, term()}
779779

780-
# Function intersection with unions and dynamic return
780+
# Function intersection with unions
781781
fun2 =
782782
intersection(
783783
fun([union(integer(), atom())], term()),
@@ -798,6 +798,25 @@ defmodule Module.Types.DescrTest do
798798
assert fun_apply(fun3, [atom([:ok])]) == {:ok, none()}
799799
end
800800

801+
test "static with dynamic signature" do
802+
assert fun_apply(fun([dynamic()], term()), [dynamic()]) == {:ok, term()}
803+
assert fun_apply(fun([integer()], dynamic()), [integer()]) == {:ok, dynamic()}
804+
805+
assert fun_apply(fun([dynamic()], integer()), [dynamic()])
806+
|> elem(1)
807+
|> equal?(integer())
808+
809+
assert fun_apply(fun([dynamic(), atom()], float()), [dynamic(), atom()])
810+
|> elem(1)
811+
|> equal?(float())
812+
813+
fun = fun([dynamic(integer())], atom())
814+
assert fun_apply(fun, [dynamic(integer())]) |> elem(1) |> equal?(atom())
815+
assert fun_apply(fun, [dynamic(number())]) == :badarg
816+
assert fun_apply(fun, [integer()]) == :badarg
817+
assert fun_apply(fun, [float()]) == :badarg
818+
end
819+
801820
defp dynamic_fun(args, return), do: dynamic(fun(args, return))
802821

803822
test "dynamic" do
@@ -1689,11 +1708,14 @@ defmodule Module.Types.DescrTest do
16891708
assert fun() |> to_quoted_string() == "fun()"
16901709
assert fun(1) |> to_quoted_string() == "(none() -> term())"
16911710

1692-
assert fun([integer(), float()], boolean()) |> to_quoted_string() ==
1693-
"(integer(), float() -> boolean())"
1711+
assert fun([dynamic(integer())], float()) |> to_quoted_string() ==
1712+
"dynamic((none() -> float())) or (integer() -> float())"
16941713

16951714
assert fun([integer(), float()], dynamic()) |> to_quoted_string() ==
1696-
"dynamic((integer(), float() -> term()))"
1715+
"dynamic((integer(), float() -> term())) or (integer(), float() -> none())"
1716+
1717+
assert fun([integer(), float()], boolean()) |> to_quoted_string() ==
1718+
"(integer(), float() -> boolean())"
16971719

16981720
assert fun([integer()], boolean())
16991721
|> union(fun([float()], boolean()))

0 commit comments

Comments
 (0)