Current section
Files
Jump to
Current section
Files
src/graded@internal@effect_term.erl
-module(graded@internal@effect_term).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/graded/internal/effect_term.gleam").
-export([pure/0, unknown/0, from_effect_set/1, free_vars/1, subst/2, normalize/1, to_effect_set/1, normalize_bounded/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(false).
-file("src/graded/internal/effect_term.gleam", 31).
?DOC(false).
-spec pure() -> graded@internal@types:effect_term().
pure() ->
{t_labels, gleam@set:new()}.
-file("src/graded/internal/effect_term.gleam", 36).
?DOC(false).
-spec unknown() -> graded@internal@types:effect_term().
unknown() ->
{t_labels, gleam@set:from_list([<<"Unknown"/utf8>>])}.
-file("src/graded/internal/effect_term.gleam", 326).
?DOC(false).
-spec dedup_adjacent(list({binary(), graded@internal@types:effect_term()})) -> list({binary(),
graded@internal@types:effect_term()}).
dedup_adjacent(Keyed) ->
_pipe = Keyed,
_pipe@1 = gleam@list:fold(_pipe, [], fun(Acc, Pair) -> case Acc of
[{Previous, _} | _] when Previous =:= erlang:element(1, Pair) ->
Acc;
_ ->
[Pair | Acc]
end end),
lists:reverse(_pipe@1).
-file("src/graded/internal/effect_term.gleam", 343).
?DOC(false).
-spec term_key(graded@internal@types:effect_term()) -> binary().
term_key(Term) ->
case Term of
t_top ->
<<"T"/utf8>>;
{t_labels, Labels} ->
<<<<"L["/utf8,
(begin
_pipe = Labels,
_pipe@1 = gleam@set:to_list(_pipe),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun gleam@string:compare/2
),
gleam@string:join(_pipe@2, <<","/utf8>>)
end)/binary>>/binary,
"]"/utf8>>;
{t_var, Name} ->
<<<<"V("/utf8, Name/binary>>/binary, ")"/utf8>>;
{t_app, Operator, Arg} ->
<<<<<<<<"A("/utf8, (term_key(Operator))/binary>>/binary, " "/utf8>>/binary,
(term_key(Arg))/binary>>/binary,
")"/utf8>>;
{t_abs, Param, Body} ->
<<<<<<<<"F("/utf8, Param/binary>>/binary, "."/utf8>>/binary,
(term_key(Body))/binary>>/binary,
")"/utf8>>;
{t_union, Terms} ->
<<<<"U("/utf8,
(begin
_pipe@3 = Terms,
_pipe@4 = gleam@list:map(_pipe@3, fun term_key/1),
_pipe@5 = gleam@list:sort(
_pipe@4,
fun gleam@string:compare/2
),
gleam@string:join(_pipe@5, <<"|"/utf8>>)
end)/binary>>/binary,
")"/utf8>>
end.
-file("src/graded/internal/effect_term.gleam", 277).
?DOC(false).
-spec flatten_union(list(graded@internal@types:effect_term())) -> graded@internal@types:effect_term().
flatten_union(Members) ->
Flat = gleam@list:flat_map(Members, fun(Member) -> case Member of
{t_union, Inner} ->
Inner;
_ ->
[Member]
end end),
case gleam@list:any(Flat, fun(M) -> M =:= t_top end) of
true ->
t_top;
false ->
{Label_members, Other_members} = gleam@list:partition(
Flat,
fun(M@1) -> case M@1 of
{t_labels, _} ->
true;
_ ->
false
end end
),
Merged_labels = gleam@list:fold(
Label_members,
gleam@set:new(),
fun(Acc, M@2) -> case M@2 of
{t_labels, Labels} ->
gleam@set:union(Acc, Labels);
_ ->
Acc
end end
),
Others = begin
_pipe = Other_members,
_pipe@1 = gleam@list:map(
_pipe,
fun(M@3) -> {term_key(M@3), M@3} end
),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun(A, B) ->
gleam@string:compare(
erlang:element(1, A),
erlang:element(1, B)
)
end
),
_pipe@3 = dedup_adjacent(_pipe@2),
gleam@list:map(
_pipe@3,
fun(Pair) -> erlang:element(2, Pair) end
)
end,
Label_part = case gleam@set:is_empty(Merged_labels) of
true ->
[];
false ->
[{t_labels, Merged_labels}]
end,
case lists:append(Label_part, Others) of
[] ->
pure();
[Single] ->
Single;
All ->
{t_union, All}
end
end.
-file("src/graded/internal/effect_term.gleam", 45).
?DOC(false).
-spec from_effect_set(graded@internal@types:effect_set()) -> graded@internal@types:effect_term().
from_effect_set(Effect_set) ->
case Effect_set of
wildcard ->
t_top;
{specific, Labels} ->
{t_labels, Labels};
{polymorphic, Labels@1, Variables} ->
Var_terms = begin
_pipe = Variables,
_pipe@1 = gleam@set:to_list(_pipe),
gleam@list:map(_pipe@1, fun(Field@0) -> {t_var, Field@0} end)
end,
flatten_union([{t_labels, Labels@1} | Var_terms])
end.
-file("src/graded/internal/effect_term.gleam", 256).
?DOC(false).
-spec is_abstraction(graded@internal@types:effect_term()) -> boolean().
is_abstraction(Term) ->
case Term of
{t_abs, _, _} ->
true;
_ ->
false
end.
-file("src/graded/internal/effect_term.gleam", 163).
?DOC(false).
-spec fresh_loop(binary(), gleam@set:set(binary()), integer()) -> binary().
fresh_loop(Base, Avoid, N) ->
Candidate = <<Base/binary, (erlang:integer_to_binary(N))/binary>>,
case gleam@set:contains(Avoid, Candidate) of
true ->
fresh_loop(Base, Avoid, N + 1);
false ->
Candidate
end.
-file("src/graded/internal/effect_term.gleam", 159).
?DOC(false).
-spec fresh(binary(), gleam@set:set(binary())) -> binary().
fresh(Base, Avoid) ->
fresh_loop(Base, Avoid, 0).
-file("src/graded/internal/effect_term.gleam", 87).
?DOC(false).
-spec free_vars(graded@internal@types:effect_term()) -> gleam@set:set(binary()).
free_vars(Term) ->
case Term of
{t_labels, _} ->
gleam@set:new();
t_top ->
gleam@set:new();
{t_var, Name} ->
gleam@set:from_list([Name]);
{t_app, Operator, Arg} ->
gleam@set:union(free_vars(Operator), free_vars(Arg));
{t_union, Terms} ->
gleam@list:fold(
Terms,
gleam@set:new(),
fun(Acc, T) -> gleam@set:union(Acc, free_vars(T)) end
);
{t_abs, Param, Body} ->
gleam@set:delete(free_vars(Body), Param)
end.
-file("src/graded/internal/effect_term.gleam", 124).
?DOC(false).
-spec subst_abs(
binary(),
graded@internal@types:effect_term(),
gleam@dict:dict(binary(), graded@internal@types:effect_term())
) -> graded@internal@types:effect_term().
subst_abs(Param, Body, Bindings) ->
Inner = gleam@dict:delete(Bindings, Param),
gleam@bool:guard(
gleam@dict:is_empty(Inner),
{t_abs, Param, Body},
fun() ->
Body_fv = free_vars(Body),
Incoming = gleam@dict:fold(
Inner,
gleam@set:new(),
fun(Acc, Key, Value) -> case gleam@set:contains(Body_fv, Key) of
true ->
gleam@set:union(Acc, free_vars(Value));
false ->
Acc
end end
),
case gleam@set:contains(Incoming, Param) of
false ->
{t_abs, Param, subst(Body, Inner)};
true ->
Avoid = gleam@set:union(Incoming, Body_fv),
Renamed_param = fresh(Param, Avoid),
Renamed_body = subst(
Body,
maps:from_list([{Param, {t_var, Renamed_param}}])
),
{t_abs, Renamed_param, subst(Renamed_body, Inner)}
end
end
).
-file("src/graded/internal/effect_term.gleam", 106).
?DOC(false).
-spec subst(
graded@internal@types:effect_term(),
gleam@dict:dict(binary(), graded@internal@types:effect_term())
) -> graded@internal@types:effect_term().
subst(Term, Bindings) ->
case Term of
{t_labels, _} ->
Term;
t_top ->
Term;
{t_var, Name} ->
case gleam_stdlib:map_get(Bindings, Name) of
{ok, Replacement} ->
Replacement;
{error, nil} ->
Term
end;
{t_app, Operator, Arg} ->
{t_app, subst(Operator, Bindings), subst(Arg, Bindings)};
{t_union, Terms} ->
{t_union,
gleam@list:map(
Terms,
fun(_capture) -> subst(_capture, Bindings) end
)};
{t_abs, Param, Body} ->
subst_abs(Param, Body, Bindings)
end.
-file("src/graded/internal/effect_term.gleam", 263).
?DOC(false).
-spec reduce_each(list(graded@internal@types:effect_term()), integer()) -> {list(graded@internal@types:effect_term()),
integer()}.
reduce_each(Terms, Fuel) ->
{Acc, Final_fuel} = gleam@list:fold(
Terms,
{[], Fuel},
fun(State, T) ->
{Done, Remaining} = State,
{Reduced, Remaining1} = reduce(T, Remaining),
{[Reduced | Done], Remaining1}
end
),
{lists:reverse(Acc), Final_fuel}.
-file("src/graded/internal/effect_term.gleam", 218).
?DOC(false).
-spec reduce_app(
graded@internal@types:effect_term(),
graded@internal@types:effect_term(),
integer()
) -> {graded@internal@types:effect_term(), integer()}.
reduce_app(Operator, Arg, Fuel) ->
{Reduced_fn, Fuel1} = reduce(Operator, Fuel - 1),
{Reduced_arg, Fuel2} = reduce(Arg, Fuel1),
case Reduced_fn of
{t_abs, Param, Body} ->
reduce(
subst(Body, maps:from_list([{Param, Reduced_arg}])),
Fuel2 - 1
);
{t_union, Members} ->
case gleam@list:all(Members, fun is_abstraction/1) of
true ->
Applied = {t_union,
gleam@list:map(
Members,
fun(M) -> {t_app, M, Reduced_arg} end
)},
reduce(Applied, Fuel2 - 1);
false ->
{{t_app, Reduced_fn, Reduced_arg}, Fuel2}
end;
_ ->
{{t_app, Reduced_fn, Reduced_arg}, Fuel2}
end.
-file("src/graded/internal/effect_term.gleam", 196).
?DOC(false).
-spec reduce(graded@internal@types:effect_term(), integer()) -> {graded@internal@types:effect_term(),
integer()}.
reduce(Term, Fuel) ->
gleam@bool:guard(Fuel =< 0, {unknown(), -1}, fun() -> case Term of
{t_labels, _} ->
{Term, Fuel};
t_top ->
{Term, Fuel};
{t_var, _} ->
{Term, Fuel};
{t_abs, Param, Body} ->
{Reduced_body, Fuel1} = reduce(Body, Fuel),
{{t_abs, Param, Reduced_body}, Fuel1};
{t_app, Operator, Arg} ->
reduce_app(Operator, Arg, Fuel);
{t_union, Terms} ->
{Reduced, Fuel1@1} = reduce_each(Terms, Fuel),
{flatten_union(Reduced), Fuel1@1}
end end).
-file("src/graded/internal/effect_term.gleam", 177).
?DOC(false).
-spec normalize(graded@internal@types:effect_term()) -> graded@internal@types:effect_term().
normalize(Term) ->
{Result, _} = reduce(Term, 1000000),
Result.
-file("src/graded/internal/effect_term.gleam", 66).
?DOC(false).
-spec term_to_set(graded@internal@types:effect_term()) -> graded@internal@types:effect_set().
term_to_set(Normalized) ->
case Normalized of
t_top ->
wildcard;
{t_labels, Labels} ->
{specific, Labels};
{t_var, Name} ->
{polymorphic, gleam@set:new(), gleam@set:from_list([Name])};
{t_app, _, _} ->
{specific, gleam@set:from_list([<<"Unknown"/utf8>>])};
{t_abs, _, _} ->
{specific, gleam@set:from_list([<<"Unknown"/utf8>>])};
{t_union, Members} ->
gleam@list:fold(
Members,
graded@internal@types:empty(),
fun(Acc, Member) ->
graded@internal@types:union(Acc, term_to_set(Member))
end
)
end.
-file("src/graded/internal/effect_term.gleam", 62).
?DOC(false).
-spec to_effect_set(graded@internal@types:effect_term()) -> graded@internal@types:effect_set().
to_effect_set(Term) ->
term_to_set(normalize(Term)).
-file("src/graded/internal/effect_term.gleam", 185).
?DOC(false).
-spec normalize_bounded(graded@internal@types:effect_term(), integer()) -> {ok,
graded@internal@types:effect_term()} |
{error, nil}.
normalize_bounded(Term, Fuel) ->
{Result, Remaining} = reduce(Term, Fuel),
case Remaining < 0 of
true ->
{error, nil};
false ->
{ok, Result}
end.