Skip to content

Commit e609d29

Browse files
committed
Type Map.pop_lazy/3
1 parent 0f3fdcd commit e609d29

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-0
lines changed

lib/elixir/lib/module/types/apply.ex

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ defmodule Module.Types.Apply do
251251
{Map, :pop, [{[open_map(), term()], tuple([term(), open_map()])}]},
252252
{Map, :pop, [{[open_map(), term(), term()], tuple([term(), open_map()])}]},
253253
{Map, :pop!, [{[open_map(), term()], tuple([term(), open_map()])}]},
254+
{Map, :pop_lazy, [{[open_map(), term(), fun(0)], tuple([term(), open_map()])}]},
254255
{Map, :put_new, [{[open_map(), term(), term()], open_map()}]},
255256
{Map, :put_new_lazy, [{[open_map(), term(), fun(0)], open_map()}]},
256257
{Map, :replace, [{[open_map(), term(), term()], open_map()}]},
@@ -488,6 +489,26 @@ defmodule Module.Types.Apply do
488489
end
489490
end
490491

492+
defp remote_apply(Map, :pop_lazy, _info, [map, key, fun] = args_types, stack) do
493+
case fun_apply(fun, []) do
494+
{:ok, default} ->
495+
case map_update(map, key, not_set(), true, false) do
496+
{value, descr, _errors} ->
497+
value = union(value, default)
498+
{:ok, return(tuple([value, descr]), args_types, stack)}
499+
500+
:badmap ->
501+
{:error, badremote(Map, :pop_lazy, args_types)}
502+
503+
{:error, _errors} ->
504+
{:error, {:badkeydomain, map, key, tuple([default, map])}}
505+
end
506+
507+
reason ->
508+
{:error, {:badapply, fun, [], reason}}
509+
end
510+
end
511+
491512
defp remote_apply(Map, :pop!, _info, [map, key] = args_types, stack) do
492513
case map_update(map, key, not_set(), true, false) do
493514
{value, descr, _errors} -> {:ok, return(tuple([value, descr]), args_types, stack)}

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

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,85 @@ defmodule Module.Types.MapTest do
598598
end
599599
end
600600

601+
describe "Map.pop_lazy/3" do
602+
test "checking" do
603+
assert typecheck!(Map.pop_lazy(%{key: 123}, :key, fn -> :error end)) ==
604+
dynamic(tuple([union(integer(), atom([:error])), empty_map()]))
605+
606+
assert typecheck!([x], Map.pop_lazy(x, :key, fn -> :error end)) ==
607+
dynamic(tuple([term(), open_map(key: not_set())]))
608+
609+
assert typecheck!(
610+
[condition?, x],
611+
Map.pop_lazy(x, if(condition?, do: :foo, else: :bar), fn -> :error end)
612+
) ==
613+
dynamic(
614+
tuple([
615+
term(),
616+
union(
617+
open_map(foo: not_set()),
618+
open_map(bar: not_set())
619+
)
620+
])
621+
)
622+
623+
assert typecheck!(
624+
[x],
625+
(
626+
x = %{String.to_integer(x) => :before}
627+
Map.pop_lazy(x, 123, fn -> :after end)
628+
)
629+
) ==
630+
dynamic(
631+
tuple([
632+
atom([:before, :after]),
633+
closed_map([{domain_key(:integer), atom([:before])}])
634+
])
635+
)
636+
end
637+
638+
test "inference" do
639+
assert typecheck!(
640+
[x],
641+
(
642+
_ = Map.pop_lazy(x, :key, fn -> :error end)
643+
x
644+
)
645+
) == dynamic(open_map())
646+
end
647+
648+
test "errors" do
649+
assert typeerror!([x = []], Map.pop_lazy(x, :foo, fn -> :error end)) =~
650+
"incompatible types given to Map.pop_lazy/3"
651+
652+
assert typeerror!(Map.pop_lazy(%{}, :key, fn -> :error end)) =~ """
653+
incompatible types given to Map.pop_lazy/3:
654+
655+
Map.pop_lazy(%{}, :key, fn -> :error end)
656+
657+
the map:
658+
659+
empty_map()
660+
661+
does not have all required keys:
662+
663+
:key
664+
665+
"""
666+
667+
assert typeerror!(Map.pop_lazy(%{}, :foo, 123)) =~
668+
"""
669+
expected a 0-arity function on function call within Map.pop_lazy/3:
670+
671+
Map.pop_lazy(%{}, :foo, 123)
672+
673+
but got type:
674+
675+
integer()
676+
"""
677+
end
678+
end
679+
601680
describe "Map.pop/3" do
602681
test "checking" do
603682
assert typecheck!(Map.pop(%{key: 123}, :key, :error)) ==

0 commit comments

Comments
 (0)