Skip to main content

src/girard@internal@infer.erl

-module(girard@internal@infer).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/girard/internal/infer.gleam").
-export([set_module/2, register_field_map/3, declare_type/3, register_type_alias/2, fresh_var/1, define/3, mark_live/2, prelude/0, prelude_interface/0, lookup/2, build_interface/6, import_qualified/3, import_value/4, import_type/4, zonk/2, unify/3, generalize/3, has_annotation_vars/1, signature_skeleton/3, bind_params/3, check_body/4, rigid_scheme/3, function_scheme/4, rigid_self_scheme/2, infer_function/3, infer_constant/3, register_custom_type/3, resolve_pending/2]).
-export_type([state/0, pending/0, env/0, module_interface/0]).

-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).

-type state() :: {state,
        integer(),
        gleam@dict:dict(integer(), girard@types:type()),
        list({glance:span(), girard@types:type()}),
        list(pending()),
        gleam@set:set(integer())}.

-type pending() :: {pending_field,
        girard@types:type(),
        binary(),
        girard@types:type()} |
    {pending_index, girard@types:type(), integer(), girard@types:type()}.

-type env() :: {env,
        gleam@dict:dict(binary(), girard@types:scheme()),
        gleam@dict:dict(binary(), girard@types:scheme()),
        gleam@dict:dict(binary(), {list(binary()), glance:type()}),
        gleam@dict:dict(binary(), {list(integer()), girard@types:type()}),
        gleam@dict:dict(binary(), gleam@dict:dict(binary(), girard@types:scheme())),
        gleam@dict:dict(binary(), {binary(), binary(), integer()}),
        gleam@dict:dict(binary(), list(gleam@option:option(binary()))),
        gleam@dict:dict(binary(), gleam@dict:dict(binary(), girard@types:type())),
        binary(),
        gleam@dict:dict(binary(), module_interface()),
        gleam@dict:dict(binary(), module_interface()),
        gleam@set:set(binary())}.

-type module_interface() :: {module_interface,
        binary(),
        gleam@dict:dict(binary(), girard@types:scheme()),
        gleam@dict:dict(binary(), {binary(), binary(), integer()}),
        gleam@dict:dict(binary(), {list(integer()), girard@types:type()}),
        gleam@dict:dict(binary(), gleam@dict:dict(binary(), girard@types:scheme())),
        gleam@dict:dict(binary(), list(gleam@option:option(binary()))),
        gleam@dict:dict(binary(), module_interface())}.

-file("src/girard/internal/infer.gleam", 145).
?DOC(false).
-spec new_state() -> state().
new_state() ->
    {state, 0, maps:new(), [], [], gleam@set:new()}.

-file("src/girard/internal/infer.gleam", 155).
?DOC(false).
-spec mark_rigid(state(), list(integer())) -> state().
mark_rigid(St, Ids) ->
    {state,
        erlang:element(2, St),
        erlang:element(3, St),
        erlang:element(4, St),
        erlang:element(5, St),
        gleam@list:fold(Ids, erlang:element(6, St), fun gleam@set:insert/2)}.

-file("src/girard/internal/infer.gleam", 159).
?DOC(false).
-spec is_rigid(state(), integer()) -> boolean().
is_rigid(St, Id) ->
    gleam@set:contains(erlang:element(6, St), Id).

-file("src/girard/internal/infer.gleam", 163).
?DOC(false).
-spec new_env() -> env().
new_env() ->
    {env,
        maps:new(),
        maps:new(),
        maps:new(),
        maps:new(),
        maps:new(),
        maps:new(),
        maps:new(),
        maps:new(),
        <<""/utf8>>,
        maps:new(),
        maps:new(),
        gleam@set:new()}.

-file("src/girard/internal/infer.gleam", 181).
?DOC(false).
-spec set_module(env(), binary()) -> env().
set_module(Env, Name) ->
    {env,
        erlang:element(2, Env),
        erlang:element(3, Env),
        erlang:element(4, Env),
        erlang:element(5, Env),
        erlang:element(6, Env),
        erlang:element(7, Env),
        erlang:element(8, Env),
        erlang:element(9, Env),
        Name,
        erlang:element(11, Env),
        erlang:element(12, Env),
        erlang:element(13, Env)}.

-file("src/girard/internal/infer.gleam", 186).
?DOC(false).
-spec register_field_map(env(), binary(), list(gleam@option:option(binary()))) -> env().
register_field_map(Env, Name, Labels) ->
    gleam@bool:guard(
        not gleam@list:any(Labels, fun(L) -> L /= none end),
        Env,
        fun() ->
            {env,
                erlang:element(2, Env),
                erlang:element(3, Env),
                erlang:element(4, Env),
                erlang:element(5, Env),
                erlang:element(6, Env),
                erlang:element(7, Env),
                gleam@dict:insert(erlang:element(8, Env), Name, Labels),
                erlang:element(9, Env),
                erlang:element(10, Env),
                erlang:element(11, Env),
                erlang:element(12, Env),
                erlang:element(13, Env)}
        end
    ).

-file("src/girard/internal/infer.gleam", 199).
?DOC(false).
-spec declare_type(env(), binary(), integer()) -> env().
declare_type(Env, Name, Arity) ->
    {env,
        erlang:element(2, Env),
        erlang:element(3, Env),
        erlang:element(4, Env),
        erlang:element(5, Env),
        erlang:element(6, Env),
        gleam@dict:insert(
            erlang:element(7, Env),
            Name,
            {erlang:element(10, Env), Name, Arity}
        ),
        erlang:element(8, Env),
        erlang:element(9, Env),
        erlang:element(10, Env),
        erlang:element(11, Env),
        erlang:element(12, Env),
        erlang:element(13, Env)}.

-file("src/girard/internal/infer.gleam", 211).
?DOC(false).
-spec register_type_alias(env(), glance:type_alias()) -> env().
register_type_alias(Env, Alias) ->
    {env,
        erlang:element(2, Env),
        erlang:element(3, Env),
        gleam@dict:insert(
            erlang:element(4, Env),
            erlang:element(3, Alias),
            {erlang:element(5, Alias), erlang:element(6, Alias)}
        ),
        erlang:element(5, Env),
        erlang:element(6, Env),
        erlang:element(7, Env),
        erlang:element(8, Env),
        erlang:element(9, Env),
        erlang:element(10, Env),
        erlang:element(11, Env),
        erlang:element(12, Env),
        erlang:element(13, Env)}.

-file("src/girard/internal/infer.gleam", 221).
?DOC(false).
-spec fresh_id(state()) -> {integer(), state()}.
fresh_id(St) ->
    {erlang:element(2, St),
        {state,
            erlang:element(2, St) + 1,
            erlang:element(3, St),
            erlang:element(4, St),
            erlang:element(5, St),
            erlang:element(6, St)}}.

-file("src/girard/internal/infer.gleam", 225).
?DOC(false).
-spec fresh(state()) -> {girard@types:type(), state()}.
fresh(St) ->
    {Id, St@1} = fresh_id(St),
    {{var, Id}, St@1}.

-file("src/girard/internal/infer.gleam", 233).
?DOC(false).
-spec var_id(girard@types:type()) -> {ok, integer()} | {error, nil}.
var_id(Type_) ->
    case Type_ of
        {var, Id} ->
            {ok, Id};

        _ ->
            {error, nil}
    end.

-file("src/girard/internal/infer.gleam", 241).
?DOC(false).
-spec fresh_var(state()) -> {girard@types:type(), state()}.
fresh_var(St) ->
    fresh(St).

-file("src/girard/internal/infer.gleam", 364).
?DOC(false).
-spec all_vars_bound(girard@types:type(), gleam@set:set(integer())) -> boolean().
all_vars_bound(Type_, Bound) ->
    case Type_ of
        {var, Id} ->
            gleam@set:contains(Bound, Id);

        {named, _, _, Args} ->
            gleam@list:all(
                Args,
                fun(_capture) -> all_vars_bound(_capture, Bound) end
            );

        {fn, Args@1, Ret} ->
            gleam@list:all(
                Args@1,
                fun(_capture@1) -> all_vars_bound(_capture@1, Bound) end
            )
            andalso all_vars_bound(Ret, Bound);

        {tuple, Elements} ->
            gleam@list:all(
                Elements,
                fun(_capture@2) -> all_vars_bound(_capture@2, Bound) end
            )
    end.

-file("src/girard/internal/infer.gleam", 359).
?DOC(false).
-spec scheme_is_closed(girard@types:scheme()) -> boolean().
scheme_is_closed(Scheme) ->
    Bound = gleam@set:from_list(erlang:element(2, Scheme)),
    all_vars_bound(erlang:element(3, Scheme), Bound).

-file("src/girard/internal/infer.gleam", 334).
?DOC(false).
-spec bind_value(env(), binary(), girard@types:scheme()) -> env().
bind_value(Env, Name, Scheme) ->
    {env,
        gleam@dict:insert(erlang:element(2, Env), Name, Scheme),
        case scheme_is_closed(Scheme) of
            true ->
                gleam@dict:delete(erlang:element(3, Env), Name);

            false ->
                gleam@dict:insert(erlang:element(3, Env), Name, Scheme)
        end,
        erlang:element(4, Env),
        erlang:element(5, Env),
        erlang:element(6, Env),
        erlang:element(7, Env),
        erlang:element(8, Env),
        gleam@dict:delete(erlang:element(9, Env), Name),
        erlang:element(10, Env),
        erlang:element(11, Env),
        erlang:element(12, Env),
        erlang:element(13, Env)}.

-file("src/girard/internal/infer.gleam", 247).
?DOC(false).
-spec define(env(), binary(), girard@types:scheme()) -> env().
define(Env, Name, Scheme) ->
    bind_value(Env, Name, Scheme).

-file("src/girard/internal/infer.gleam", 254).
?DOC(false).
-spec mark_live(env(), list(binary())) -> env().
mark_live(Env, Names) ->
    {env,
        erlang:element(2, Env),
        erlang:element(3, Env),
        erlang:element(4, Env),
        erlang:element(5, Env),
        erlang:element(6, Env),
        erlang:element(7, Env),
        erlang:element(8, Env),
        erlang:element(9, Env),
        erlang:element(10, Env),
        erlang:element(11, Env),
        erlang:element(12, Env),
        gleam@set:from_list(Names)}.

-file("src/girard/internal/infer.gleam", 260).
?DOC(false).
-spec prelude() -> {env(), state()}.
prelude() ->
    St = new_state(),
    Env = begin
        _pipe = new_env(),
        _pipe@1 = bind_value(
            _pipe,
            <<"True"/utf8>>,
            {scheme, [], girard@internal@prelude:bool()}
        ),
        _pipe@2 = bind_value(
            _pipe@1,
            <<"False"/utf8>>,
            {scheme, [], girard@internal@prelude:bool()}
        ),
        bind_value(
            _pipe@2,
            <<"Nil"/utf8>>,
            {scheme, [], girard@internal@prelude:nil()}
        )
    end,
    {Ok_a, St@1} = fresh_id(St),
    {Ok_e, St@2} = fresh_id(St@1),
    Env@1 = bind_value(
        Env,
        <<"Ok"/utf8>>,
        {scheme,
            [Ok_a, Ok_e],
            {fn,
                [{var, Ok_a}],
                girard@internal@prelude:result({var, Ok_a}, {var, Ok_e})}}
    ),
    {Err_a, St@3} = fresh_id(St@2),
    {Err_e, St@4} = fresh_id(St@3),
    Env@2 = bind_value(
        Env@1,
        <<"Error"/utf8>>,
        {scheme,
            [Err_a, Err_e],
            {fn,
                [{var, Err_e}],
                girard@internal@prelude:result({var, Err_a}, {var, Err_e})}}
    ),
    {Env@2, St@4}.

-file("src/girard/internal/infer.gleam", 301).
?DOC(false).
-spec prelude_interface() -> module_interface().
prelude_interface() ->
    Module = <<"gleam"/utf8>>,
    Values = maps:from_list(
        [{<<"True"/utf8>>, {scheme, [], girard@internal@prelude:bool()}},
            {<<"False"/utf8>>, {scheme, [], girard@internal@prelude:bool()}},
            {<<"Nil"/utf8>>, {scheme, [], girard@internal@prelude:nil()}},
            {<<"Ok"/utf8>>,
                {scheme,
                    [0, 1],
                    {fn,
                        [{var, 0}],
                        girard@internal@prelude:result({var, 0}, {var, 1})}}},
            {<<"Error"/utf8>>,
                {scheme,
                    [0, 1],
                    {fn,
                        [{var, 1}],
                        girard@internal@prelude:result({var, 0}, {var, 1})}}}]
    ),
    Types_ = maps:from_list(
        [{<<"Int"/utf8>>, {Module, <<"Int"/utf8>>, 0}},
            {<<"Float"/utf8>>, {Module, <<"Float"/utf8>>, 0}},
            {<<"String"/utf8>>, {Module, <<"String"/utf8>>, 0}},
            {<<"Bool"/utf8>>, {Module, <<"Bool"/utf8>>, 0}},
            {<<"Nil"/utf8>>, {Module, <<"Nil"/utf8>>, 0}},
            {<<"BitArray"/utf8>>, {Module, <<"BitArray"/utf8>>, 0}},
            {<<"UtfCodepoint"/utf8>>, {Module, <<"UtfCodepoint"/utf8>>, 0}},
            {<<"List"/utf8>>, {Module, <<"List"/utf8>>, 1}},
            {<<"Result"/utf8>>, {Module, <<"Result"/utf8>>, 2}}]
    ),
    {module_interface,
        Module,
        Values,
        Types_,
        maps:new(),
        maps:new(),
        maps:new(),
        maps:new()}.

-file("src/girard/internal/infer.gleam", 375).
?DOC(false).
-spec lookup(env(), binary()) -> {ok, girard@types:scheme()} | {error, nil}.
lookup(Env, Name) ->
    gleam_stdlib:map_get(erlang:element(2, Env), Name).

-file("src/girard/internal/infer.gleam", 437).
?DOC(false).
-spec take(gleam@dict:dict(binary(), GQW), list(binary())) -> gleam@dict:dict(binary(), GQW).
take(D, Keys) ->
    gleam@list:fold(
        Keys,
        maps:new(),
        fun(Acc, Key) -> case gleam_stdlib:map_get(D, Key) of
                {ok, Value} ->
                    gleam@dict:insert(Acc, Key, Value);

                {error, _} ->
                    Acc
            end end
    ).

-file("src/girard/internal/infer.gleam", 802).
?DOC(false).
-spec substitute(
    gleam@dict:dict(integer(), girard@types:type()),
    girard@types:type()
) -> girard@types:type().
substitute(Mapping, Type_) ->
    case Type_ of
        {var, Id} ->
            case gleam_stdlib:map_get(Mapping, Id) of
                {ok, Replacement} ->
                    Replacement;

                {error, _} ->
                    Type_
            end;

        {named, Module, Name, Args} ->
            {named,
                Module,
                Name,
                gleam@list:map(
                    Args,
                    fun(_capture) -> substitute(Mapping, _capture) end
                )};

        {fn, Args@1, Ret} ->
            {fn,
                gleam@list:map(
                    Args@1,
                    fun(_capture@1) -> substitute(Mapping, _capture@1) end
                ),
                substitute(Mapping, Ret)};

        {tuple, Elements} ->
            {tuple,
                gleam@list:map(
                    Elements,
                    fun(_capture@2) -> substitute(Mapping, _capture@2) end
                )}
    end.

-file("src/girard/internal/infer.gleam", 429).
?DOC(false).
-spec instantiate_alias(
    list(integer()),
    girard@types:type(),
    list(girard@types:type())
) -> girard@types:type().
instantiate_alias(Params, Body, Arguments) ->
    substitute(maps:from_list(gleam@list:zip(Params, Arguments)), Body).

-file("src/girard/internal/infer.gleam", 2511).
?DOC(false).
-spec resolve_qualified_type(
    env(),
    state(),
    binary(),
    binary(),
    list(girard@types:type())
) -> {girard@types:type(), state()}.
resolve_qualified_type(Env, St, Alias, Name, Arg_types) ->
    case gleam_stdlib:map_get(erlang:element(11, Env), Alias) of
        {error, _} ->
            {{named, Alias, Name, Arg_types}, St};

        {ok, Interface} ->
            case gleam_stdlib:map_get(erlang:element(5, Interface), Name) of
                {ok, {Params, Body}} ->
                    {instantiate_alias(Params, Body, Arg_types), St};

                {error, _} ->
                    Origin@1 = case gleam_stdlib:map_get(
                        erlang:element(4, Interface),
                        Name
                    ) of
                        {ok, {Origin, _, _}} ->
                            Origin;

                        {error, _} ->
                            Alias
                    end,
                    {{named, Origin@1, Name, Arg_types}, St}
            end
    end.

-file("src/girard/internal/infer.gleam", 2497).
?DOC(false).
-spec resolve_named_origin(env(), binary(), list(girard@types:type())) -> girard@types:type().
resolve_named_origin(Env, Name, Arg_types) ->
    case gleam_stdlib:map_get(erlang:element(5, Env), Name) of
        {ok, {Params, Body}} ->
            instantiate_alias(Params, Body, Arg_types);

        {error, _} ->
            case gleam_stdlib:map_get(erlang:element(7, Env), Name) of
                {ok, {Origin, Origin_name, _}} ->
                    {named, Origin, Origin_name, Arg_types};

                {error, _} ->
                    {named, <<"gleam"/utf8>>, Name, Arg_types}
            end
    end.

-file("src/girard/internal/infer.gleam", 2478).
?DOC(false).
-spec resolve_unqualified_type(
    env(),
    state(),
    binary(),
    list(girard@types:type())
) -> {girard@types:type(), state()}.
resolve_unqualified_type(Env, St, Name, Arg_types) ->
    case gleam_stdlib:map_get(erlang:element(4, Env), Name) of
        {ok, {Params, Aliased}} ->
            Alias_names = maps:from_list(gleam@list:zip(Params, Arg_types)),
            {{T, St@1}, _} = hydrate_with(Env, Alias_names, St, Aliased),
            {T, St@1};

        {error, _} ->
            {resolve_named_origin(Env, Name, Arg_types), St}
    end.

-file("src/girard/internal/infer.gleam", 2465).
?DOC(false).
-spec resolve_named_type(
    env(),
    state(),
    gleam@option:option(binary()),
    binary(),
    list(girard@types:type())
) -> {girard@types:type(), state()}.
resolve_named_type(Env, St, Module, Name, Arg_types) ->
    case Module of
        none ->
            resolve_unqualified_type(Env, St, Name, Arg_types);

        {some, Alias} ->
            resolve_qualified_type(Env, St, Alias, Name, Arg_types)
    end.

-file("src/girard/internal/infer.gleam", 2534).
?DOC(false).
-spec hydrate_with(
    env(),
    gleam@dict:dict(binary(), girard@types:type()),
    state(),
    glance:type()
) -> {{girard@types:type(), state()},
    gleam@dict:dict(binary(), girard@types:type())}.
hydrate_with(Env, Names, St, Ast) ->
    case Ast of
        {named_type, _, Name, Module, Parameters} ->
            {Arg_types, St@3, Names@3} = gleam@list:fold(
                Parameters,
                {[], St, Names},
                fun(Acc, P) ->
                    {Types_, St@1, Names@1} = Acc,
                    {{T, St@2}, Names@2} = hydrate_with(Env, Names@1, St@1, P),
                    {[T | Types_], St@2, Names@2}
                end
            ),
            Arg_types@1 = lists:reverse(Arg_types),
            {T@1, St@4} = resolve_named_type(
                Env,
                St@3,
                Module,
                Name,
                Arg_types@1
            ),
            {{T@1, St@4}, Names@3};

        {tuple_type, _, Elements} ->
            {Elem_types, St@7, Names@6} = gleam@list:fold(
                Elements,
                {[], St, Names},
                fun(Acc@1, E) ->
                    {Types_@1, St@5, Names@4} = Acc@1,
                    {{T@2, St@6}, Names@5} = hydrate_with(Env, Names@4, St@5, E),
                    {[T@2 | Types_@1], St@6, Names@5}
                end
            ),
            {{{tuple, lists:reverse(Elem_types)}, St@7}, Names@6};

        {function_type, _, Parameters@1, Return} ->
            {Param_types, St@10, Names@9} = gleam@list:fold(
                Parameters@1,
                {[], St, Names},
                fun(Acc@2, P@1) ->
                    {Types_@2, St@8, Names@7} = Acc@2,
                    {{T@3, St@9}, Names@8} = hydrate_with(
                        Env,
                        Names@7,
                        St@8,
                        P@1
                    ),
                    {[T@3 | Types_@2], St@9, Names@8}
                end
            ),
            {{Ret, St@11}, Names@10} = hydrate_with(Env, Names@9, St@10, Return),
            {{{fn, lists:reverse(Param_types), Ret}, St@11}, Names@10};

        {variable_type, _, Name@1} ->
            case gleam_stdlib:map_get(Names, Name@1) of
                {ok, T@4} ->
                    {{T@4, St}, Names};

                {error, _} ->
                    {T@5, St@12} = fresh(St),
                    {{T@5, St@12}, gleam@dict:insert(Names, Name@1, T@5)}
            end;

        {hole_type, _, _} ->
            {T@6, St@13} = fresh(St),
            {{T@6, St@13}, Names}
    end.

-file("src/girard/internal/infer.gleam", 3020).
?DOC(false).
-spec hydrate_in(
    env(),
    gleam@dict:dict(binary(), girard@types:type()),
    state(),
    glance:type()
) -> {girard@types:type(), state()}.
hydrate_in(Env, Names, St, Ast) ->
    erlang:element(1, hydrate_with(Env, Names, St, Ast)).

-file("src/girard/internal/infer.gleam", 1493).
?DOC(false).
-spec fresh_n(state(), integer()) -> {list(girard@types:type()), state()}.
fresh_n(St, N) ->
    gleam@bool:guard(
        N =< 0,
        {[], St},
        fun() ->
            {T, St@1} = fresh(St),
            {Rest, St@2} = fresh_n(St@1, N - 1),
            {[T | Rest], St@2}
        end
    ).

-file("src/girard/internal/infer.gleam", 408).
?DOC(false).
-spec resolve_aliases(env(), state(), list(binary())) -> gleam@dict:dict(binary(), {list(integer()),
    girard@types:type()}).
resolve_aliases(Env, St, Type_names) ->
    erlang:element(
        1,
        gleam@list:fold(
            Type_names,
            {maps:new(), St},
            fun(Acc, Name) ->
                {Resolved, St@1} = Acc,
                case gleam_stdlib:map_get(erlang:element(4, Env), Name) of
                    {error, _} ->
                        {Resolved, St@1};

                    {ok, {Params, Body}} ->
                        {Param_vars, St@2} = fresh_n(
                            St@1,
                            erlang:length(Params)
                        ),
                        Param_ids = gleam@list:filter_map(
                            Param_vars,
                            fun var_id/1
                        ),
                        Names = maps:from_list(
                            gleam@list:zip(Params, Param_vars)
                        ),
                        {Type_, St@3} = hydrate_in(Env, Names, St@2, Body),
                        {gleam@dict:insert(Resolved, Name, {Param_ids, Type_}),
                            St@3}
                end
            end
        )
    ).

-file("src/girard/internal/infer.gleam", 383).
?DOC(false).
-spec build_interface(
    env(),
    state(),
    binary(),
    list(binary()),
    list(binary()),
    list(binary())
) -> module_interface().
build_interface(Env, St, Name, Value_names, Type_names, Accessor_type_names) ->
    {module_interface,
        Name,
        take(erlang:element(2, Env), Value_names),
        take(erlang:element(7, Env), Type_names),
        resolve_aliases(Env, St, Type_names),
        take(erlang:element(6, Env), Accessor_type_names),
        take(erlang:element(8, Env), Value_names),
        erlang:element(11, Env)}.

-file("src/girard/internal/infer.gleam", 473).
?DOC(false).
-spec index_interface(
    gleam@dict:dict(binary(), module_interface()),
    module_interface()
) -> gleam@dict:dict(binary(), module_interface()).
index_interface(Index, Interface) ->
    gleam@bool:guard(
        gleam@dict:has_key(Index, erlang:element(2, Interface)),
        Index,
        fun() ->
            gleam@dict:fold(
                erlang:element(8, Interface),
                gleam@dict:insert(
                    Index,
                    erlang:element(2, Interface),
                    Interface
                ),
                fun(Acc, _, Nested) -> index_interface(Acc, Nested) end
            )
        end
    ).

-file("src/girard/internal/infer.gleam", 447).
?DOC(false).
-spec import_qualified(env(), binary(), module_interface()) -> env().
import_qualified(Env, Alias, Interface) ->
    Modules = gleam@dict:fold(
        erlang:element(11, Env),
        erlang:element(8, Interface),
        fun(Acc, Alias@1, Interface@1) ->
            gleam@dict:insert(Acc, Alias@1, Interface@1)
        end
    ),
    {env,
        erlang:element(2, Env),
        erlang:element(3, Env),
        erlang:element(4, Env),
        erlang:element(5, Env),
        erlang:element(6, Env),
        erlang:element(7, Env),
        erlang:element(8, Env),
        erlang:element(9, Env),
        erlang:element(10, Env),
        gleam@dict:insert(Modules, Alias, Interface),
        index_interface(erlang:element(12, Env), Interface),
        erlang:element(13, Env)}.

-file("src/girard/internal/infer.gleam", 487).
?DOC(false).
-spec import_value(env(), binary(), module_interface(), binary()) -> env().
import_value(Env, Local, Interface, Original) ->
    Env@1 = case gleam_stdlib:map_get(erlang:element(3, Interface), Original) of
        {ok, Scheme} ->
            bind_value(Env, Local, Scheme);

        {error, _} ->
            Env
    end,
    case gleam_stdlib:map_get(erlang:element(7, Interface), Original) of
        {ok, Field_map} ->
            {env,
                erlang:element(2, Env@1),
                erlang:element(3, Env@1),
                erlang:element(4, Env@1),
                erlang:element(5, Env@1),
                erlang:element(6, Env@1),
                erlang:element(7, Env@1),
                gleam@dict:insert(erlang:element(8, Env@1), Local, Field_map),
                erlang:element(9, Env@1),
                erlang:element(10, Env@1),
                erlang:element(11, Env@1),
                erlang:element(12, Env@1),
                erlang:element(13, Env@1)};

        {error, _} ->
            Env@1
    end.

-file("src/girard/internal/infer.gleam", 505).
?DOC(false).
-spec import_type(env(), binary(), module_interface(), binary()) -> env().
import_type(Env, Local, Interface, Original) ->
    Env@1 = case gleam_stdlib:map_get(erlang:element(4, Interface), Original) of
        {ok, Info} ->
            {env,
                erlang:element(2, Env),
                erlang:element(3, Env),
                erlang:element(4, Env),
                erlang:element(5, Env),
                erlang:element(6, Env),
                gleam@dict:insert(erlang:element(7, Env), Local, Info),
                erlang:element(8, Env),
                erlang:element(9, Env),
                erlang:element(10, Env),
                erlang:element(11, Env),
                erlang:element(12, Env),
                erlang:element(13, Env)};

        {error, _} ->
            Env
    end,
    Env@2 = case gleam_stdlib:map_get(erlang:element(5, Interface), Original) of
        {ok, Alias} ->
            {env,
                erlang:element(2, Env@1),
                erlang:element(3, Env@1),
                erlang:element(4, Env@1),
                gleam@dict:insert(erlang:element(5, Env@1), Local, Alias),
                erlang:element(6, Env@1),
                erlang:element(7, Env@1),
                erlang:element(8, Env@1),
                erlang:element(9, Env@1),
                erlang:element(10, Env@1),
                erlang:element(11, Env@1),
                erlang:element(12, Env@1),
                erlang:element(13, Env@1)};

        {error, _} ->
            Env@1
    end,
    case gleam_stdlib:map_get(erlang:element(6, Interface), Original) of
        {ok, Accessors} ->
            {env,
                erlang:element(2, Env@2),
                erlang:element(3, Env@2),
                erlang:element(4, Env@2),
                erlang:element(5, Env@2),
                gleam@dict:insert(erlang:element(6, Env@2), Local, Accessors),
                erlang:element(7, Env@2),
                erlang:element(8, Env@2),
                erlang:element(9, Env@2),
                erlang:element(10, Env@2),
                erlang:element(11, Env@2),
                erlang:element(12, Env@2),
                erlang:element(13, Env@2)};

        {error, _} ->
            Env@2
    end.

-file("src/girard/internal/infer.gleam", 534).
?DOC(false).
-spec resolve(state(), girard@types:type()) -> girard@types:type().
resolve(St, Type_) ->
    case Type_ of
        {var, Id} ->
            case gleam_stdlib:map_get(erlang:element(3, St), Id) of
                {ok, Bound} ->
                    resolve(St, Bound);

                {error, _} ->
                    Type_
            end;

        _ ->
            Type_
    end.

-file("src/girard/internal/infer.gleam", 546).
?DOC(false).
-spec zonk(state(), girard@types:type()) -> girard@types:type().
zonk(St, Type_) ->
    case resolve(St, Type_) of
        {named, Module, Name, Args} ->
            {named,
                Module,
                Name,
                gleam@list:map(Args, fun(_capture) -> zonk(St, _capture) end)};

        {fn, Args@1, Ret} ->
            {fn,
                gleam@list:map(
                    Args@1,
                    fun(_capture@1) -> zonk(St, _capture@1) end
                ),
                zonk(St, Ret)};

        {tuple, Elements} ->
            {tuple,
                gleam@list:map(
                    Elements,
                    fun(_capture@2) -> zonk(St, _capture@2) end
                )};

        {var, Id} ->
            {var, Id}
    end.

-file("src/girard/internal/infer.gleam", 560).
?DOC(false).
-spec free_vars_loop(girard@types:type(), list(integer())) -> list(integer()).
free_vars_loop(Type_, Acc) ->
    case Type_ of
        {var, Id} ->
            case gleam@list:contains(Acc, Id) of
                true ->
                    Acc;

                false ->
                    [Id | Acc]
            end;

        {named, _, _, Args} ->
            gleam@list:fold(Args, Acc, fun(A, T) -> free_vars_loop(T, A) end);

        {fn, Args@1, Ret} ->
            free_vars_loop(
                Ret,
                gleam@list:fold(
                    Args@1,
                    Acc,
                    fun(A@1, T@1) -> free_vars_loop(T@1, A@1) end
                )
            );

        {tuple, Elements} ->
            gleam@list:fold(
                Elements,
                Acc,
                fun(A@2, T@2) -> free_vars_loop(T@2, A@2) end
            )
    end.

-file("src/girard/internal/infer.gleam", 556).
?DOC(false).
-spec free_vars(state(), girard@types:type()) -> list(integer()).
free_vars(St, Type_) ->
    free_vars_loop(zonk(St, Type_), []).

-file("src/girard/internal/infer.gleam", 597).
?DOC(false).
-spec scheme_free_vars(
    state(),
    girard@types:type(),
    list(integer()),
    list(integer())
) -> list(integer()).
scheme_free_vars(St, Type_, Bound, Acc) ->
    case Type_ of
        {var, Id} ->
            case gleam@list:contains(Bound, Id) of
                true ->
                    Acc;

                false ->
                    case resolve(St, Type_) of
                        {var, Resolved} ->
                            case gleam@list:contains(Acc, Resolved) of
                                true ->
                                    Acc;

                                false ->
                                    [Resolved | Acc]
                            end;

                        Other ->
                            scheme_free_vars(St, Other, Bound, Acc)
                    end
            end;

        {named, _, _, Args} ->
            gleam@list:fold(
                Args,
                Acc,
                fun(A, T) -> scheme_free_vars(St, T, Bound, A) end
            );

        {fn, Args@1, Ret} ->
            scheme_free_vars(
                St,
                Ret,
                Bound,
                gleam@list:fold(
                    Args@1,
                    Acc,
                    fun(A@1, T@1) -> scheme_free_vars(St, T@1, Bound, A@1) end
                )
            );

        {tuple, Elements} ->
            gleam@list:fold(
                Elements,
                Acc,
                fun(A@2, T@2) -> scheme_free_vars(St, T@2, Bound, A@2) end
            )
    end.

-file("src/girard/internal/infer.gleam", 578).
?DOC(false).
-spec env_free_vars(state(), env()) -> list(integer()).
env_free_vars(St, Env) ->
    gleam@dict:fold(
        erlang:element(3, Env),
        [],
        fun(Acc, _, Scheme) ->
            scheme_free_vars(
                St,
                erlang:element(3, Scheme),
                erlang:element(2, Scheme),
                Acc
            )
        end
    ).

-file("src/girard/internal/infer.gleam", 694).
?DOC(false).
-spec occurs(state(), integer(), girard@types:type()) -> boolean().
occurs(St, Id, Type_) ->
    gleam@list:contains(free_vars(St, Type_), Id).

-file("src/girard/internal/infer.gleam", 686).
?DOC(false).
-spec bind_var(state(), integer(), girard@types:type()) -> {ok, state()} |
    {error, girard@types:error()}.
bind_var(St, Id, Type_) ->
    gleam@bool:guard(
        occurs(St, Id, Type_),
        {error, {recursive_type, Id, Type_}},
        fun() ->
            {ok,
                {state,
                    erlang:element(2, St),
                    gleam@dict:insert(erlang:element(3, St), Id, Type_),
                    erlang:element(4, St),
                    erlang:element(5, St),
                    erlang:element(6, St)}}
        end
    ).

-file("src/girard/internal/infer.gleam", 671).
?DOC(false).
-spec unify_many(state(), list(girard@types:type()), list(girard@types:type())) -> {ok,
        state()} |
    {error, girard@types:error()}.
unify_many(St, Left, Right) ->
    case {Left, Right} of
        {[], []} ->
            {ok, St};

        {[X | Xs], [Y | Ys]} ->
            gleam@result:'try'(
                unify(St, X, Y),
                fun(St@1) -> unify_many(St@1, Xs, Ys) end
            );

        {_, _} ->
            {error, arity_mismatch}
    end.

-file("src/girard/internal/infer.gleam", 633).
?DOC(false).
-spec unify(state(), girard@types:type(), girard@types:type()) -> {ok, state()} |
    {error, girard@types:error()}.
unify(St, Left, Right) ->
    Left@1 = resolve(St, Left),
    Right@1 = resolve(St, Right),
    case {Left@1, Right@1} of
        {{var, I}, {var, J}} when I =:= J ->
            {ok, St};

        {{var, I@1}, {var, J@1}} ->
            case {is_rigid(St, I@1), is_rigid(St, J@1)} of
                {true, true} ->
                    {error, {type_mismatch, Left@1, Right@1}};

                {true, false} ->
                    bind_var(St, J@1, Left@1);

                {false, _} ->
                    bind_var(St, I@1, Right@1)
            end;

        {{var, I@2}, Other} ->
            case is_rigid(St, I@2) of
                true ->
                    {error, {type_mismatch, Left@1, Right@1}};

                false ->
                    bind_var(St, I@2, Other)
            end;

        {Other@1, {var, J@2}} ->
            case is_rigid(St, J@2) of
                true ->
                    {error, {type_mismatch, Left@1, Right@1}};

                false ->
                    bind_var(St, J@2, Other@1)
            end;

        {{named, M1, N1, A1}, {named, M2, N2, A2}} when (M1 =:= M2) andalso (N1 =:= N2) ->
            unify_many(St, A1, A2);

        {{fn, Args1, R1}, {fn, Args2, R2}} ->
            gleam@result:'try'(
                unify_many(St, Args1, Args2),
                fun(St@1) -> unify(St@1, R1, R2) end
            );

        {{tuple, E1}, {tuple, E2}} ->
            unify_many(St, E1, E2);

        {_, _} ->
            {error, {type_mismatch, Left@1, Right@1}}
    end.

-file("src/girard/internal/infer.gleam", 702).
?DOC(false).
-spec generalize(state(), env(), girard@types:type()) -> girard@types:scheme().
generalize(St, Env, Type_) ->
    Zonked = zonk(St, Type_),
    Env_vars = env_free_vars(St, Env),
    Quantified = gleam@list:filter(
        free_vars(St, Zonked),
        fun(Id) -> not gleam@list:contains(Env_vars, Id) end
    ),
    {scheme, Quantified, Zonked}.

-file("src/girard/internal/infer.gleam", 713).
?DOC(false).
-spec generalize_over(state(), env(), girard@types:type(), list(integer())) -> girard@types:scheme().
generalize_over(St, Env, Type_, Candidate_ids) ->
    Zonked = zonk(St, Type_),
    Env_vars = env_free_vars(St, Env),
    Free = free_vars(St, Zonked),
    Reps = gleam@list:filter_map(
        Candidate_ids,
        fun(Id) -> case zonk(St, {var, Id}) of
                {var, Rep} ->
                    {ok, Rep};

                _ ->
                    {error, nil}
            end end
    ),
    Quantified = gleam@list:unique(
        gleam@list:filter(
            Reps,
            fun(Id@1) ->
                gleam@list:contains(Free, Id@1) andalso not gleam@list:contains(
                    Env_vars,
                    Id@1
                )
            end
        )
    ),
    {scheme, Quantified, Zonked}.

-file("src/girard/internal/infer.gleam", 758).
?DOC(false).
-spec type_var_names(glance:type()) -> list(binary()).
type_var_names(Ast) ->
    case Ast of
        {variable_type, _, Name} ->
            [Name];

        {named_type, _, _, _, Parameters} ->
            gleam@list:flat_map(Parameters, fun type_var_names/1);

        {tuple_type, _, Elements} ->
            gleam@list:flat_map(Elements, fun type_var_names/1);

        {function_type, _, Parameters@1, Return} ->
            lists:append(
                gleam@list:flat_map(Parameters@1, fun type_var_names/1),
                type_var_names(Return)
            );

        {hole_type, _, _} ->
            []
    end.

-file("src/girard/internal/infer.gleam", 741).
?DOC(false).
-spec fn_annotation_var_names(
    list(glance:fn_parameter()),
    gleam@option:option(glance:type())
) -> list(binary()).
fn_annotation_var_names(Params, Return_annotation) ->
    From_params = gleam@list:flat_map(
        Params,
        fun(P) -> case erlang:element(3, P) of
                {some, T} ->
                    type_var_names(T);

                none ->
                    []
            end end
    ),
    case Return_annotation of
        {some, T@1} ->
            lists:append(From_params, type_var_names(T@1));

        none ->
            From_params
    end.

-file("src/girard/internal/infer.gleam", 774).
?DOC(false).
-spec instantiate(state(), girard@types:scheme()) -> {girard@types:type(),
    state()}.
instantiate(St, Scheme) ->
    {Mapping@1, St@3} = gleam@list:fold(
        erlang:element(2, Scheme),
        {maps:new(), St},
        fun(Acc, Old) ->
            {Mapping, St@1} = Acc,
            {Fresh_type, St@2} = fresh(St@1),
            {gleam@dict:insert(Mapping, Old, Fresh_type), St@2}
        end
    ),
    {substitute(Mapping@1, erlang:element(3, Scheme)), St@3}.

-file("src/girard/internal/infer.gleam", 790).
?DOC(false).
-spec instantiate_in(env(), state(), binary(), girard@types:scheme()) -> {girard@types:type(),
    state()}.
instantiate_in(Env, St, Name, Scheme) ->
    case gleam@set:contains(erlang:element(13, Env), Name) of
        true ->
            instantiate(
                St,
                {scheme,
                    erlang:element(2, Scheme),
                    zonk(St, erlang:element(3, Scheme))}
            );

        false ->
            instantiate(St, Scheme)
    end.

-file("src/girard/internal/infer.gleam", 3047).
?DOC(false).
-spec span(glance:expression()) -> glance:span().
span(Expr) ->
    case Expr of
        {int, S, _} ->
            S;

        {float, S@1, _} ->
            S@1;

        {string, S@2, _} ->
            S@2;

        {variable, S@3, _} ->
            S@3;

        {negate_int, S@4, _} ->
            S@4;

        {negate_bool, S@5, _} ->
            S@5;

        {block, S@6, _} ->
            S@6;

        {panic, S@7, _} ->
            S@7;

        {todo, S@8, _} ->
            S@8;

        {tuple, S@9, _} ->
            S@9;

        {list, S@10, _, _} ->
            S@10;

        {fn, S@11, _, _, _} ->
            S@11;

        {record_update, S@12, _, _, _, _} ->
            S@12;

        {field_access, S@13, _, _} ->
            S@13;

        {call, S@14, _, _} ->
            S@14;

        {tuple_index, S@15, _, _} ->
            S@15;

        {fn_capture, S@16, _, _, _, _} ->
            S@16;

        {bit_string, S@17, _} ->
            S@17;

        {'case', S@18, _, _} ->
            S@18;

        {binary_operator, S@19, _, _, _} ->
            S@19;

        {echo, S@20, _, _} ->
            S@20
    end.

-file("src/girard/internal/infer.gleam", 3043).
?DOC(false).
-spec record(state(), glance:span(), girard@types:type()) -> state().
record(St, Span, Type_) ->
    {state,
        erlang:element(2, St),
        erlang:element(3, St),
        [{Span, Type_} | erlang:element(4, St)],
        erlang:element(5, St),
        erlang:element(6, St)}.

-file("src/girard/internal/infer.gleam", 1213).
?DOC(false).
-spec constructor_field_map(env(), gleam@option:option(binary()), binary()) -> list(gleam@option:option(binary())).
constructor_field_map(Env, Module, Constructor) ->
    Maps = case Module of
        {some, Alias} ->
            case gleam_stdlib:map_get(erlang:element(11, Env), Alias) of
                {ok, Interface} ->
                    erlang:element(7, Interface);

                {error, _} ->
                    erlang:element(8, Env)
            end;

        none ->
            erlang:element(8, Env)
    end,
    case gleam_stdlib:map_get(Maps, Constructor) of
        {ok, Labels} ->
            Labels;

        {error, _} ->
            []
    end.

-file("src/girard/internal/infer.gleam", 2330).
?DOC(false).
-spec constructor_scheme(env(), gleam@option:option(binary()), binary()) -> {ok,
        girard@types:scheme()} |
    {error, girard@types:error()}.
constructor_scheme(Env, Module, Constructor) ->
    case Module of
        {some, Alias} ->
            case gleam_stdlib:map_get(erlang:element(11, Env), Alias) of
                {ok, Interface} ->
                    case gleam_stdlib:map_get(
                        erlang:element(3, Interface),
                        Constructor
                    ) of
                        {ok, Scheme} ->
                            {ok, Scheme};

                        {error, _} ->
                            {error, {no_such_export, Alias, Constructor}}
                    end;

                {error, _} ->
                    {error, {unknown_module, Alias}}
            end;

        none ->
            case gleam_stdlib:map_get(erlang:element(2, Env), Constructor) of
                {ok, Scheme@1} ->
                    {ok, Scheme@1};

                {error, _} ->
                    {error, {unknown_constructor, Constructor}}
            end
    end.

-file("src/girard/internal/infer.gleam", 1268).
?DOC(false).
-spec record_variant(
    env(),
    state(),
    gleam@option:option(binary()),
    binary(),
    girard@types:type(),
    binary()
) -> {env(), state()}.
record_variant(Env, St, Module, Constructor, Value_type, Name) ->
    Recorded = begin
        gleam@result:'try'(
            constructor_scheme(Env, Module, Constructor),
            fun(Scheme) ->
                {Ctor_type, St@1} = instantiate(St, Scheme),
                {Field_types, Ret@1} = case Ctor_type of
                    {fn, Args, Ret} ->
                        {Args, Ret};

                    Other ->
                        {[], Other}
                end,
                gleam@result:'try'(
                    unify(St@1, Value_type, Ret@1),
                    fun(St@2) ->
                        Labels = constructor_field_map(Env, Module, Constructor),
                        Fields = gleam@list:fold(
                            gleam@list:zip(Labels, Field_types),
                            maps:new(),
                            fun(Acc, Pair) -> case erlang:element(1, Pair) of
                                    {some, Label} ->
                                        gleam@dict:insert(
                                            Acc,
                                            Label,
                                            resolve(
                                                St@2,
                                                erlang:element(2, Pair)
                                            )
                                        );

                                    none ->
                                        Acc
                                end end
                        ),
                        {ok, {Fields, St@2}}
                    end
                )
            end
        )
    end,
    case Recorded of
        {ok, {Fields@1, St@3}} ->
            {{env,
                    erlang:element(2, Env),
                    erlang:element(3, Env),
                    erlang:element(4, Env),
                    erlang:element(5, Env),
                    erlang:element(6, Env),
                    erlang:element(7, Env),
                    erlang:element(8, Env),
                    gleam@dict:insert(erlang:element(9, Env), Name, Fields@1),
                    erlang:element(10, Env),
                    erlang:element(11, Env),
                    erlang:element(12, Env),
                    erlang:element(13, Env)},
                St@3};

        {error, _} ->
            {Env, St}
    end.

-file("src/girard/internal/infer.gleam", 1257).
?DOC(false).
-spec is_upper(binary()) -> boolean().
is_upper(Name) ->
    case gleam@string:first(Name) of
        {ok, C} ->
            (string:uppercase(C) =:= C) andalso (string:lowercase(C) /= C);

        {error, _} ->
            false
    end.

-file("src/girard/internal/infer.gleam", 1235).
?DOC(false).
-spec constructor_call(glance:expression()) -> {ok,
        {gleam@option:option(binary()), binary()}} |
    {error, nil}.
constructor_call(Expr) ->
    Callee = case Expr of
        {call, _, Function, _} ->
            Function;

        _ ->
            Expr
    end,
    case Callee of
        {variable, _, Name} ->
            case is_upper(Name) of
                true ->
                    {ok, {none, Name}};

                false ->
                    {error, nil}
            end;

        {field_access, _, {variable, _, Module}, Name@1} ->
            case is_upper(Name@1) of
                true ->
                    {ok, {{some, Module}, Name@1}};

                false ->
                    {error, nil}
            end;

        _ ->
            {error, nil}
    end.

-file("src/girard/internal/infer.gleam", 2356).
?DOC(false).
-spec segment_value_type(
    list(glance:bit_string_segment_option(any())),
    girard@types:type()
) -> girard@types:type().
segment_value_type(Options, Default) ->
    gleam@list:fold(Options, Default, fun(Acc, Option) -> case Option of
                float_option ->
                    girard@internal@prelude:float();

                utf8_option ->
                    girard@internal@prelude:string();

                utf16_option ->
                    girard@internal@prelude:string();

                utf32_option ->
                    girard@internal@prelude:string();

                utf8_codepoint_option ->
                    girard@internal@prelude:utf_codepoint();

                utf16_codepoint_option ->
                    girard@internal@prelude:utf_codepoint();

                utf32_codepoint_option ->
                    girard@internal@prelude:utf_codepoint();

                bytes_option ->
                    girard@internal@prelude:bit_array();

                bits_option ->
                    girard@internal@prelude:bit_array();

                int_option ->
                    girard@internal@prelude:int();

                _ ->
                    Acc
            end end).

-file("src/girard/internal/infer.gleam", 2448).
?DOC(false).
-spec indices_loop(integer(), list(integer())) -> list(integer()).
indices_loop(I, Acc) ->
    gleam@bool:guard(I < 0, Acc, fun() -> indices_loop(I - 1, [I | Acc]) end).

-file("src/girard/internal/infer.gleam", 2444).
?DOC(false).
-spec indices(integer()) -> list(integer()).
indices(N) ->
    indices_loop(N - 1, []).

-file("src/girard/internal/infer.gleam", 1613).
?DOC(false).
-spec label_index(gleam@dict:dict(binary(), integer()), binary()) -> {ok,
        integer()} |
    {error, girard@types:error()}.
label_index(Index_of, Label) ->
    case gleam_stdlib:map_get(Index_of, Label) of
        {ok, Index} ->
            {ok, Index};

        {error, _} ->
            {error, {unknown_label, Label}}
    end.

-file("src/girard/internal/infer.gleam", 2377).
?DOC(false).
-spec order_pattern_args(
    env(),
    gleam@option:option(binary()),
    binary(),
    list(glance:field(glance:pattern())),
    integer()
) -> {ok, list(glance:pattern())} | {error, girard@types:error()}.
order_pattern_args(Env, Module, Constructor, Arguments, Arity) ->
    Field_maps = case Module of
        {some, Alias} ->
            case gleam_stdlib:map_get(erlang:element(11, Env), Alias) of
                {ok, Interface} ->
                    erlang:element(7, Interface);

                {error, _} ->
                    erlang:element(8, Env)
            end;

        none ->
            erlang:element(8, Env)
    end,
    Labels@1 = case gleam_stdlib:map_get(Field_maps, Constructor) of
        {ok, Labels} ->
            Labels;

        {error, _} ->
            []
    end,
    Index_of = gleam@list:index_fold(
        Labels@1,
        maps:new(),
        fun(Acc, Label, Index) -> case Label of
                {some, Name} ->
                    gleam@dict:insert(Acc, Name, Index);

                none ->
                    Acc
            end end
    ),
    gleam@result:'try'(
        gleam@list:try_fold(
            Arguments,
            maps:new(),
            fun(Placed, Field) -> case Field of
                    {unlabelled_field, _} ->
                        {ok, Placed};

                    {labelled_field, Label@1, _, Item} ->
                        gleam@result:'try'(
                            label_index(Index_of, Label@1),
                            fun(Index@1) ->
                                {ok, gleam@dict:insert(Placed, Index@1, Item)}
                            end
                        );

                    {shorthand_field, Label@2, Location} ->
                        gleam@result:'try'(
                            label_index(Index_of, Label@2),
                            fun(Index@2) ->
                                {ok,
                                    gleam@dict:insert(
                                        Placed,
                                        Index@2,
                                        {pattern_variable, Location, Label@2}
                                    )}
                            end
                        )
                end end
        ),
        fun(Labelled) ->
            Positional = gleam@list:filter_map(
                Arguments,
                fun(Field@1) -> case Field@1 of
                        {unlabelled_field, Item@1} ->
                            {ok, Item@1};

                        _ ->
                            {error, nil}
                    end end
            ),
            Free = gleam@list:filter(
                indices(Arity),
                fun(I) -> not gleam@dict:has_key(Labelled, I) end
            ),
            Placed@2 = gleam@list:fold(
                gleam@list:zip(Free, Positional),
                Labelled,
                fun(Placed@1, Pair) ->
                    gleam@dict:insert(
                        Placed@1,
                        erlang:element(1, Pair),
                        erlang:element(2, Pair)
                    )
                end
            ),
            {ok,
                gleam@list:map(
                    indices(Arity),
                    fun(Index@3) ->
                        case gleam_stdlib:map_get(Placed@2, Index@3) of
                            {ok, Pattern} ->
                                Pattern;

                            {error, _} ->
                                {pattern_discard, {span, 0, 0}, <<"_"/utf8>>}
                        end
                    end
                )}
        end
    ).

-file("src/girard/internal/infer.gleam", 2322).
?DOC(false).
-spec with_env(env(), {ok, state()} | {error, girard@types:error()}) -> {ok,
        {env(), state()}} |
    {error, girard@types:error()}.
with_env(Env, St) ->
    gleam@result:map(St, fun(St@1) -> {Env, St@1} end).

-file("src/girard/internal/infer.gleam", 2291).
?DOC(false).
-spec infer_bit_pattern_segment(
    env(),
    state(),
    {glance:pattern(), list(glance:bit_string_segment_option(glance:pattern()))}
) -> {ok, {env(), state()}} | {error, girard@types:error()}.
infer_bit_pattern_segment(Env, St, Segment) ->
    {Pattern, Options} = Segment,
    Default = case Pattern of
        {pattern_string, _, _} ->
            girard@internal@prelude:string();

        {pattern_float, _, _} ->
            girard@internal@prelude:float();

        _ ->
            girard@internal@prelude:int()
    end,
    gleam@result:'try'(
        infer_pattern(Env, St, Pattern, segment_value_type(Options, Default)),
        fun(_use0) ->
            {Env@1, St@1} = _use0,
            gleam@list:try_fold(
                Options,
                {Env@1, St@1},
                fun(Acc, Option) ->
                    {Env@2, St@2} = Acc,
                    case Option of
                        {size_value_option, Size} ->
                            infer_pattern(
                                Env@2,
                                St@2,
                                Size,
                                girard@internal@prelude:int()
                            );

                        _ ->
                            {ok, {Env@2, St@2}}
                    end
                end
            )
        end
    ).

-file("src/girard/internal/infer.gleam", 2175).
?DOC(false).
-spec infer_pattern(env(), state(), glance:pattern(), girard@types:type()) -> {ok,
        {env(), state()}} |
    {error, girard@types:error()}.
infer_pattern(Env, St, Pattern, Expected) ->
    case Pattern of
        {pattern_int, _, _} ->
            with_env(Env, unify(St, Expected, girard@internal@prelude:int()));

        {pattern_float, _, _} ->
            with_env(Env, unify(St, Expected, girard@internal@prelude:float()));

        {pattern_string, _, _} ->
            with_env(Env, unify(St, Expected, girard@internal@prelude:string()));

        {pattern_discard, _, _} ->
            {ok, {Env, St}};

        {pattern_variable, _, Name} ->
            {ok, {bind_value(Env, Name, {scheme, [], Expected}), St}};

        {pattern_tuple, _, Elements} ->
            {Elem_types, St@3} = gleam@list:fold(
                Elements,
                {[], St},
                fun(Acc, _) ->
                    {Types_, St@1} = Acc,
                    {T, St@2} = fresh(St@1),
                    {[T | Types_], St@2}
                end
            ),
            Elem_types@1 = lists:reverse(Elem_types),
            gleam@result:'try'(
                unify(St@3, Expected, {tuple, Elem_types@1}),
                fun(St@4) ->
                    gleam@list:try_fold(
                        gleam@list:zip(Elements, Elem_types@1),
                        {Env, St@4},
                        fun(Acc@1, Pair) ->
                            {Env@1, St@5} = Acc@1,
                            {Pattern@1, T@1} = Pair,
                            infer_pattern(Env@1, St@5, Pattern@1, T@1)
                        end
                    )
                end
            );

        {pattern_list, _, Elements@1, Tail} ->
            {Elem, St@6} = fresh(St),
            gleam@result:'try'(
                unify(St@6, Expected, girard@internal@prelude:list(Elem)),
                fun(St@7) ->
                    gleam@result:'try'(
                        gleam@list:try_fold(
                            Elements@1,
                            {Env, St@7},
                            fun(Acc@2, P) ->
                                {Env@2, St@8} = Acc@2,
                                infer_pattern(Env@2, St@8, P, Elem)
                            end
                        ),
                        fun(_use0) ->
                            {Env@3, St@9} = _use0,
                            case Tail of
                                {some, T@2} ->
                                    infer_pattern(
                                        Env@3,
                                        St@9,
                                        T@2,
                                        girard@internal@prelude:list(Elem)
                                    );

                                none ->
                                    {ok, {Env@3, St@9}}
                            end
                        end
                    )
                end
            );

        {pattern_assignment, _, Pattern@2, Name@1} ->
            Env@4 = bind_value(Env, Name@1, {scheme, [], Expected}),
            {Env@5, St@10} = case Pattern@2 of
                {pattern_variant, _, Module, Constructor, _, _} ->
                    record_variant(
                        Env@4,
                        St,
                        Module,
                        Constructor,
                        Expected,
                        Name@1
                    );

                _ ->
                    {Env@4, St}
            end,
            infer_pattern(Env@5, St@10, Pattern@2, Expected);

        {pattern_concatenate, _, _, Prefix_name, Rest_name} ->
            gleam@result:'try'(
                unify(St, Expected, girard@internal@prelude:string()),
                fun(St@11) ->
                    Bind_name = fun(Env@6, Name@2) -> case Name@2 of
                            {named, N} ->
                                bind_value(
                                    Env@6,
                                    N,
                                    {scheme,
                                        [],
                                        girard@internal@prelude:string()}
                                );

                            {discarded, _} ->
                                Env@6
                        end end,
                    Env@7 = case Prefix_name of
                        {some, Name@3} ->
                            Bind_name(Env, Name@3);

                        none ->
                            Env
                    end,
                    {ok, {Bind_name(Env@7, Rest_name), St@11}}
                end
            );

        {pattern_variant, _, Module@1, Constructor@1, Arguments, _} ->
            gleam@result:'try'(
                constructor_scheme(Env, Module@1, Constructor@1),
                fun(Scheme) ->
                    {Ctor_type, St@12} = instantiate(St, Scheme),
                    {Field_types, Ret@1} = case Ctor_type of
                        {fn, Args, Ret} ->
                            {Args, Ret};

                        Other ->
                            {[], Other}
                    end,
                    gleam@result:'try'(
                        unify(St@12, Expected, Ret@1),
                        fun(St@13) ->
                            gleam@result:'try'(
                                order_pattern_args(
                                    Env,
                                    Module@1,
                                    Constructor@1,
                                    Arguments,
                                    erlang:length(Field_types)
                                ),
                                fun(Arg_patterns) ->
                                    gleam@list:try_fold(
                                        gleam@list:zip(
                                            Arg_patterns,
                                            Field_types
                                        ),
                                        {Env, St@13},
                                        fun(Acc@3, Pair@1) ->
                                            {Env@8, St@14} = Acc@3,
                                            {Pattern@3, T@3} = Pair@1,
                                            infer_pattern(
                                                Env@8,
                                                St@14,
                                                Pattern@3,
                                                T@3
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            );

        {pattern_bit_string, _, Segments} ->
            gleam@result:'try'(
                unify(St, Expected, girard@internal@prelude:bit_array()),
                fun(St@15) ->
                    gleam@list:try_fold(
                        Segments,
                        {Env, St@15},
                        fun(Acc@4, Segment) ->
                            {Env@9, St@16} = Acc@4,
                            infer_bit_pattern_segment(Env@9, St@16, Segment)
                        end
                    )
                end
            )
    end.

-file("src/girard/internal/infer.gleam", 2458).
?DOC(false).
-spec hydrate(env(), state(), glance:type()) -> {girard@types:type(), state()}.
hydrate(Env, St, Ast) ->
    erlang:element(1, hydrate_with(Env, maps:new(), St, Ast)).

-file("src/girard/internal/infer.gleam", 1551).
?DOC(false).
-spec label_indices(list(gleam@option:option(binary()))) -> gleam@dict:dict(binary(), integer()).
label_indices(Labels) ->
    gleam@list:index_fold(
        Labels,
        maps:new(),
        fun(Acc, Label, Index) -> case Label of
                {some, Name} ->
                    gleam@dict:insert(Acc, Name, Index);

                none ->
                    Acc
            end end
    ).

-file("src/girard/internal/infer.gleam", 1535).
?DOC(false).
-spec callee_labels(env(), glance:expression()) -> {ok,
        list(gleam@option:option(binary()))} |
    {error, nil}.
callee_labels(Env, Callee) ->
    case Callee of
        {variable, _, Name} ->
            gleam_stdlib:map_get(erlang:element(8, Env), Name);

        {field_access, _, {variable, _, Alias}, Name@1} ->
            case gleam_stdlib:map_get(erlang:element(11, Env), Alias) of
                {ok, Interface} ->
                    gleam_stdlib:map_get(erlang:element(7, Interface), Name@1);

                {error, _} ->
                    gleam_stdlib:map_get(erlang:element(8, Env), Name@1)
            end;

        _ ->
            {error, nil}
    end.

-file("src/girard/internal/infer.gleam", 1500).
?DOC(false).
-spec field_item(glance:field(glance:expression())) -> glance:expression().
field_item(Field) ->
    case Field of
        {unlabelled_field, Item} ->
            Item;

        {labelled_field, _, _, Item@1} ->
            Item@1;

        {shorthand_field, Label, Location} ->
            {variable, Location, Label}
    end.

-file("src/girard/internal/infer.gleam", 1560).
?DOC(false).
-spec is_unlabelled(glance:field(any())) -> boolean().
is_unlabelled(Field) ->
    case Field of
        {unlabelled_field, _} ->
            true;

        _ ->
            false
    end.

-file("src/girard/internal/infer.gleam", 2913).
?DOC(false).
-spec accessors_of_module(env(), binary()) -> gleam@dict:dict(binary(), gleam@dict:dict(binary(), girard@types:scheme())).
accessors_of_module(Env, Module) ->
    case Module =:= erlang:element(10, Env) of
        true ->
            erlang:element(6, Env);

        false ->
            case gleam_stdlib:map_get(erlang:element(12, Env), Module) of
                {ok, Interface} ->
                    erlang:element(6, Interface);

                {error, _} ->
                    erlang:element(6, Env)
            end
    end.

-file("src/girard/internal/infer.gleam", 2896).
?DOC(false).
-spec accessor(env(), girard@types:type(), binary()) -> {ok,
        girard@types:scheme()} |
    {error, girard@types:error()}.
accessor(Env, Record, Label) ->
    case Record of
        {named, Module, Name, _} ->
            Accessors = accessors_of_module(Env, Module),
            case gleam_stdlib:map_get(Accessors, Name) of
                {ok, Labels} ->
                    case gleam_stdlib:map_get(Labels, Label) of
                        {ok, Scheme} ->
                            {ok, Scheme};

                        {error, _} ->
                            {error, {no_such_field, Name, Label}}
                    end;

                {error, _} ->
                    {error, {no_such_field, Name, Label}}
            end;

        _ ->
            {error, not_a_record}
    end.

-file("src/girard/internal/infer.gleam", 1120).
?DOC(false).
-spec field_type(env(), state(), girard@types:type(), binary()) -> {ok,
        {girard@types:type(), state()}} |
    {error, girard@types:error()}.
field_type(Env, St, Record, Label) ->
    gleam@result:'try'(
        accessor(Env, resolve(St, Record), Label),
        fun(Accessor_scheme) ->
            {Accessor_type, St@1} = instantiate(St, Accessor_scheme),
            {Field, St@2} = fresh(St@1),
            gleam@result:'try'(
                unify(St@2, Accessor_type, {fn, [Record], Field}),
                fun(St@3) -> {ok, {Field, St@3}} end
            )
        end
    ).

-file("src/girard/internal/infer.gleam", 3073).
?DOC(false).
-spec list_at(list(HAM), integer()) -> {ok, HAM} | {error, nil}.
list_at(Items, Index) ->
    case {Items, Index} of
        {[X | _], 0} ->
            {ok, X};

        {[_ | Rest], N} when N > 0 ->
            list_at(Rest, N - 1);

        {_, _} ->
            {error, nil}
    end.

-file("src/girard/internal/infer.gleam", 1567).
?DOC(false).
-spec reorder(
    list(glance:field(GVD)),
    list(gleam@option:option(binary())),
    fun((binary(), glance:span()) -> GVD)
) -> {ok, list(GVD)} | {error, girard@types:error()}.
reorder(Fields, Labels, Shorthand) ->
    Index_of = label_indices(Labels),
    gleam@result:'try'(
        gleam@list:try_fold(
            Fields,
            maps:new(),
            fun(Placed, Field) -> case Field of
                    {unlabelled_field, _} ->
                        {ok, Placed};

                    {labelled_field, Label, _, Item} ->
                        gleam@result:'try'(
                            label_index(Index_of, Label),
                            fun(Index) ->
                                {ok, gleam@dict:insert(Placed, Index, Item)}
                            end
                        );

                    {shorthand_field, Label@1, Location} ->
                        gleam@result:'try'(
                            label_index(Index_of, Label@1),
                            fun(Index@1) ->
                                {ok,
                                    gleam@dict:insert(
                                        Placed,
                                        Index@1,
                                        Shorthand(Label@1, Location)
                                    )}
                            end
                        )
                end end
        ),
        fun(Labelled) ->
            Positional = gleam@list:filter_map(
                Fields,
                fun(Field@1) -> case Field@1 of
                        {unlabelled_field, Item@1} ->
                            {ok, Item@1};

                        _ ->
                            {error, nil}
                    end end
            ),
            Free = gleam@list:filter(
                indices(erlang:length(Labels)),
                fun(I) -> not gleam@dict:has_key(Labelled, I) end
            ),
            Placed@2 = gleam@list:fold(
                gleam@list:zip(Free, Positional),
                Labelled,
                fun(Placed@1, Pair) ->
                    gleam@dict:insert(
                        Placed@1,
                        erlang:element(1, Pair),
                        erlang:element(2, Pair)
                    )
                end
            ),
            gleam@list:try_map(
                indices(erlang:length(Labels)),
                fun(Index@2) -> case gleam_stdlib:map_get(Placed@2, Index@2) of
                        {ok, Item@2} ->
                            {ok, Item@2};

                        {error, _} ->
                            {error, missing_argument}
                    end end
            )
        end
    ).

-file("src/girard/internal/infer.gleam", 1511).
?DOC(false).
-spec order_fields(
    env(),
    glance:expression(),
    list(glance:field(GUN)),
    fun((binary(), glance:span()) -> GUN)
) -> {ok, list(GUN)} | {error, girard@types:error()}.
order_fields(Env, Callee, Fields, Shorthand) ->
    gleam@bool:lazy_guard(
        gleam@list:all(Fields, fun is_unlabelled/1),
        fun() -> {ok, gleam@list:filter_map(Fields, fun(Field) -> case Field of
                            {unlabelled_field, Item} ->
                                {ok, Item};

                            _ ->
                                {error, nil}
                        end end)} end,
        fun() -> case callee_labels(Env, Callee) of
                {ok, Labels} ->
                    reorder(Fields, Labels, Shorthand);

                {error, _} ->
                    {error, ambiguous_call}
            end end
    ).

-file("src/girard/internal/infer.gleam", 1452).
?DOC(false).
-spec field_label_is_fn(env(), state(), girard@types:type(), binary()) -> boolean().
field_label_is_fn(Env, St, Record, Label) ->
    case field_type(Env, St, Record, Label) of
        {error, _} ->
            false;

        {ok, {Field, St@1}} ->
            case resolve(St@1, Field) of
                {fn, _, _} ->
                    true;

                _ ->
                    false
            end
    end.

-file("src/girard/internal/infer.gleam", 1438).
?DOC(false).
-spec field_is_callable(env(), state(), binary(), binary()) -> boolean().
field_is_callable(Env, St, Name, Label) ->
    case gleam_stdlib:map_get(erlang:element(2, Env), Name) of
        {error, _} ->
            false;

        {ok, Scheme} ->
            {Value_type, St@1} = instantiate(St, Scheme),
            case resolve(St@1, Value_type) of
                {named, _, _, _} = Record ->
                    field_label_is_fn(Env, St@1, Record, Label);

                _ ->
                    false
            end
    end.

-file("src/girard/internal/infer.gleam", 2065).
?DOC(false).
-spec infer_expr_assignment(
    env(),
    state(),
    glance:pattern(),
    gleam@option:option(glance:type()),
    glance:expression()
) -> {ok, {girard@types:type(), env(), state()}} | {error, girard@types:error()}.
infer_expr_assignment(Env, St, Pattern, Annotation, Value) ->
    gleam@result:'try'(
        infer_expr(Env, St, Value),
        fun(_use0) ->
            {Value_type, St@1} = _use0,
            gleam@result:'try'(case Annotation of
                    {some, Ann} ->
                        {T, St@2} = hydrate(Env, St@1, Ann),
                        unify(St@2, Value_type, T);

                    none ->
                        {ok, St@1}
                end, fun(St@3) ->
                    gleam@result:'try'(
                        infer_pattern(Env, St@3, Pattern, Value_type),
                        fun(_use0@1) ->
                            {Env@1, St@4} = _use0@1,
                            {Env@2, St@5} = case {Pattern, Value} of
                                {{pattern_variable, _, Name}, _} ->
                                    case constructor_call(Value) of
                                        {ok, {Module, Constructor}} ->
                                            record_variant(
                                                Env@1,
                                                St@4,
                                                Module,
                                                Constructor,
                                                Value_type,
                                                Name
                                            );

                                        {error, _} ->
                                            {Env@1, St@4}
                                    end;

                                {{pattern_variant,
                                        _,
                                        Module@1,
                                        Constructor@1,
                                        _,
                                        _},
                                    {variable, _, Name@1}} ->
                                    record_variant(
                                        Env@1,
                                        St@4,
                                        Module@1,
                                        Constructor@1,
                                        Value_type,
                                        Name@1
                                    );

                                {_, _} ->
                                    {Env@1, St@4}
                            end,
                            {ok, {Value_type, Env@2, St@5}}
                        end
                    )
                end)
        end
    ).

-file("src/girard/internal/infer.gleam", 2001).
?DOC(false).
-spec infer_statement(env(), state(), glance:statement()) -> {ok,
        {girard@types:type(), env(), state()}} |
    {error, girard@types:error()}.
infer_statement(Env, St, Statement) ->
    case Statement of
        {expression, Expr} ->
            gleam@result:'try'(
                infer_expr(Env, St, Expr),
                fun(_use0) ->
                    {T, St@1} = _use0,
                    {ok, {T, Env, St@1}}
                end
            );

        {assignment,
            _,
            _,
            {pattern_variable, _, Name} = Pattern,
            none,
            {fn, Fspan, Fparams, Freturn, Fbody} = Value} ->
            case fn_annotation_var_names(Fparams, Freturn) of
                [] ->
                    infer_expr_assignment(Env, St, Pattern, none, Value);

                Ann_names ->
                    {Names@1, Ann_ids, St@4} = gleam@list:fold(
                        Ann_names,
                        {maps:new(), [], St},
                        fun(Acc, Nm) ->
                            {Names, Ids, St@2} = Acc,
                            {Id, St@3} = fresh_id(St@2),
                            {gleam@dict:insert(Names, Nm, {var, Id}),
                                [Id | Ids],
                                St@3}
                        end
                    ),
                    {Seeds, St@5} = fresh_n(St@4, erlang:length(Fparams)),
                    gleam@result:'try'(
                        infer_lambda(
                            Env,
                            St@5,
                            Fparams,
                            Freturn,
                            Fbody,
                            Seeds,
                            none,
                            Names@1
                        ),
                        fun(_use0@1) ->
                            {Value_type, St@6} = _use0@1,
                            St@7 = record(St@6, Fspan, Value_type),
                            Scheme = generalize_over(
                                St@7,
                                Env,
                                Value_type,
                                Ann_ids
                            ),
                            {ok,
                                {Value_type,
                                    bind_value(Env, Name, Scheme),
                                    St@7}}
                        end
                    )
            end;

        {assignment, _, _, Pattern@1, Annotation, Value@1} ->
            infer_expr_assignment(Env, St, Pattern@1, Annotation, Value@1);

        {assert, _, Expression, _} ->
            gleam@result:'try'(
                check(Env, St, Expression, girard@internal@prelude:bool()),
                fun(St@8) ->
                    {ok, {girard@internal@prelude:nil(), Env, St@8}}
                end
            );

        {use, _, _, _} ->
            {error, {unsupported, <<"use in non-tail position"/utf8>>}}
    end.

-file("src/girard/internal/infer.gleam", 1625).
?DOC(false).
-spec classify_call_arg(
    env(),
    gleam@dict:dict(binary(), integer()),
    {gleam@dict:dict(integer(), girard@types:type()),
        list(girard@types:type()),
        state()},
    glance:field(glance:expression())
) -> {ok,
        {gleam@dict:dict(integer(), girard@types:type()),
            list(girard@types:type()),
            state()}} |
    {error, girard@types:error()}.
classify_call_arg(Env, Index_of, Acc, Field) ->
    {Labelled, Positional, St} = Acc,
    case Field of
        {unlabelled_field, Item} ->
            gleam@result:'try'(
                infer_expr(Env, St, Item),
                fun(_use0) ->
                    {T, St@1} = _use0,
                    {ok, {Labelled, [T | Positional], St@1}}
                end
            );

        {labelled_field, Label, _, Item@1} ->
            gleam@result:'try'(
                label_index(Index_of, Label),
                fun(Index) ->
                    gleam@result:'try'(
                        infer_expr(Env, St, Item@1),
                        fun(_use0@1) ->
                            {T@1, St@2} = _use0@1,
                            {ok,
                                {gleam@dict:insert(Labelled, Index, T@1),
                                    Positional,
                                    St@2}}
                        end
                    )
                end
            );

        {shorthand_field, Label@1, Location} ->
            gleam@result:'try'(
                label_index(Index_of, Label@1),
                fun(Index@1) ->
                    gleam@result:'try'(
                        infer_expr(Env, St, {variable, Location, Label@1}),
                        fun(_use0@2) ->
                            {T@2, St@3} = _use0@2,
                            {ok,
                                {gleam@dict:insert(Labelled, Index@1, T@2),
                                    Positional,
                                    St@3}}
                        end
                    )
                end
            )
    end.

-file("src/girard/internal/infer.gleam", 1305).
?DOC(false).
-spec infer_each(env(), state(), list(glance:expression())) -> {ok,
        {list(girard@types:type()), state()}} |
    {error, girard@types:error()}.
infer_each(Env, St, Exprs) ->
    gleam@result:'try'(
        gleam@list:try_fold(
            Exprs,
            {[], St},
            fun(Acc, E) ->
                {Types_, St@1} = Acc,
                gleam@result:'try'(
                    infer_expr(Env, St@1, E),
                    fun(_use0) ->
                        {T, St@2} = _use0,
                        {ok, {[T | Types_], St@2}}
                    end
                )
            end
        ),
        fun(_use0@1) ->
            {Rev, St@3} = _use0@1,
            {ok, {lists:reverse(Rev), St@3}}
        end
    ).

-file("src/girard/internal/infer.gleam", 1945).
?DOC(false).
-spec infer_use_call(
    env(),
    state(),
    glance:expression(),
    list(glance:field(glance:expression())),
    girard@types:type(),
    girard@types:type()
) -> {ok, state()} | {error, girard@types:error()}.
infer_use_call(Env, St, Callee, Arguments, Callback_type, Result) ->
    gleam@result:'try'(
        infer_expr(Env, St, Callee),
        fun(_use0) ->
            {Callee_type, St@1} = _use0,
            case gleam@list:all(Arguments, fun is_unlabelled/1) of
                true ->
                    gleam@result:'try'(
                        infer_each(
                            Env,
                            St@1,
                            gleam@list:map(Arguments, fun field_item/1)
                        ),
                        fun(_use0@1) ->
                            {Arg_types, St@2} = _use0@1,
                            unify(
                                St@2,
                                Callee_type,
                                {fn,
                                    lists:append(Arg_types, [Callback_type]),
                                    Result}
                            )
                        end
                    );

                false ->
                    gleam@result:'try'(
                        gleam@result:replace_error(
                            callee_labels(Env, Callee),
                            ambiguous_call
                        ),
                        fun(Labels) ->
                            Index_of = label_indices(Labels),
                            gleam@result:'try'(
                                gleam@list:try_fold(
                                    Arguments,
                                    {maps:new(), [], St@1},
                                    fun(Acc, Field) ->
                                        classify_call_arg(
                                            Env,
                                            Index_of,
                                            Acc,
                                            Field
                                        )
                                    end
                                ),
                                fun(_use0@2) ->
                                    {Labelled, Rev_positional, St@3} = _use0@2,
                                    Trailing = lists:append(
                                        lists:reverse(Rev_positional),
                                        [Callback_type]
                                    ),
                                    Free = gleam@list:filter(
                                        indices(erlang:length(Labels)),
                                        fun(I) ->
                                            not gleam@dict:has_key(Labelled, I)
                                        end
                                    ),
                                    Placed@1 = gleam@list:fold(
                                        gleam@list:zip(Free, Trailing),
                                        Labelled,
                                        fun(Placed, Pair) ->
                                            gleam@dict:insert(
                                                Placed,
                                                erlang:element(1, Pair),
                                                erlang:element(2, Pair)
                                            )
                                        end
                                    ),
                                    gleam@result:'try'(
                                        gleam@list:try_map(
                                            indices(erlang:length(Labels)),
                                            fun(Index) ->
                                                gleam@result:replace_error(
                                                    gleam_stdlib:map_get(
                                                        Placed@1,
                                                        Index
                                                    ),
                                                    missing_argument
                                                )
                                            end
                                        ),
                                        fun(Arg_types@1) ->
                                            unify(
                                                St@3,
                                                Callee_type,
                                                {fn, Arg_types@1, Result}
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
            end
        end
    ).

-file("src/girard/internal/infer.gleam", 1899).
?DOC(false).
-spec infer_use(
    env(),
    state(),
    list(glance:use_pattern()),
    glance:expression(),
    list(glance:statement())
) -> {ok, {girard@types:type(), state()}} | {error, girard@types:error()}.
infer_use(Env, St, Use_patterns, Function, Rest) ->
    gleam@result:'try'(
        gleam@list:try_fold(
            Use_patterns,
            {[], Env, St},
            fun(Acc, Use_pattern) ->
                {Types_, Env@1, St@1} = Acc,
                {Param, St@2} = case erlang:element(3, Use_pattern) of
                    {some, Ann} ->
                        hydrate(Env@1, St@1, Ann);

                    none ->
                        fresh(St@1)
                end,
                gleam@result:'try'(
                    infer_pattern(
                        Env@1,
                        St@2,
                        erlang:element(2, Use_pattern),
                        Param
                    ),
                    fun(_use0) ->
                        {Env@2, St@3} = _use0,
                        {ok, {[Param | Types_], Env@2, St@3}}
                    end
                )
            end
        ),
        fun(_use0@1) ->
            {Rev_param_types, Callback_env, St@4} = _use0@1,
            Param_types = lists:reverse(Rev_param_types),
            gleam@result:'try'(
                infer_statements(Callback_env, St@4, Rest),
                fun(_use0@2) ->
                    {Body_type, St@5} = _use0@2,
                    Callback_type = {fn, Param_types, Body_type},
                    {Result, St@6} = fresh(St@5),
                    gleam@result:'try'(case Function of
                            {call, _, Callee, Arguments} ->
                                infer_use_call(
                                    Env,
                                    St@6,
                                    Callee,
                                    Arguments,
                                    Callback_type,
                                    Result
                                );

                            Other ->
                                gleam@result:'try'(
                                    infer_expr(Env, St@6, Other),
                                    fun(_use0@3) ->
                                        {Callee_type, St@7} = _use0@3,
                                        unify(
                                            St@7,
                                            Callee_type,
                                            {fn, [Callback_type], Result}
                                        )
                                    end
                                )
                        end, fun(St@8) -> {ok, {Result, St@8}} end)
                end
            )
        end
    ).

-file("src/girard/internal/infer.gleam", 1876).
?DOC(false).
-spec infer_statements(env(), state(), list(glance:statement())) -> {ok,
        {girard@types:type(), state()}} |
    {error, girard@types:error()}.
infer_statements(Env, St, Statements) ->
    case Statements of
        [] ->
            {ok, {girard@internal@prelude:nil(), St}};

        [{use, _, Patterns, Function} | Rest] ->
            infer_use(Env, St, Patterns, Function, Rest);

        [Last] ->
            gleam@result:'try'(
                infer_statement(Env, St, Last),
                fun(_use0) ->
                    {T, _, St@1} = _use0,
                    {ok, {T, St@1}}
                end
            );

        [First | Rest@1] ->
            gleam@result:'try'(
                infer_statement(Env, St, First),
                fun(_use0@1) ->
                    {_, Env@1, St@2} = _use0@1,
                    infer_statements(Env@1, St@2, Rest@1)
                end
            )
    end.

-file("src/girard/internal/infer.gleam", 1352).
?DOC(false).
-spec infer_lambda(
    env(),
    state(),
    list(glance:fn_parameter()),
    gleam@option:option(glance:type()),
    list(glance:statement()),
    list(girard@types:type()),
    gleam@option:option(girard@types:type()),
    gleam@dict:dict(binary(), girard@types:type())
) -> {ok, {girard@types:type(), state()}} | {error, girard@types:error()}.
infer_lambda(
    Env,
    St,
    Params,
    Return_annotation,
    Body,
    Seed_params,
    Expected_return,
    Names
) ->
    gleam@result:'try'(
        gleam@list:try_fold(
            gleam@list:zip(Params, Seed_params),
            {[], Env, St},
            fun(Acc, Pair) ->
                {Types_, Env@1, St@1} = Acc,
                {Param, Seed} = Pair,
                gleam@result:'try'(case erlang:element(3, Param) of
                        {some, Ann} ->
                            {Annotated, St@2} = hydrate_in(
                                Env@1,
                                Names,
                                St@1,
                                Ann
                            ),
                            gleam@result:'try'(
                                unify(St@2, Annotated, Seed),
                                fun(St@3) -> {ok, {Seed, St@3}} end
                            );

                        none ->
                            {ok, {Seed, St@1}}
                    end, fun(_use0) ->
                        {T, St@4} = _use0,
                        Env@2 = case erlang:element(2, Param) of
                            {named, Name} ->
                                bind_value(Env@1, Name, {scheme, [], T});

                            {discarded, _} ->
                                Env@1
                        end,
                        {ok, {[T | Types_], Env@2, St@4}}
                    end)
            end
        ),
        fun(_use0@1) ->
            {Rev_param_types, Body_env, St@5} = _use0@1,
            Param_types = lists:reverse(Rev_param_types),
            gleam@result:'try'(
                infer_statements(Body_env, St@5, Body),
                fun(_use0@2) ->
                    {Body_type, St@6} = _use0@2,
                    gleam@result:'try'(case Return_annotation of
                            {some, Ann@1} ->
                                {T@1, St@7} = hydrate_in(
                                    Env,
                                    Names,
                                    St@6,
                                    Ann@1
                                ),
                                unify(St@7, Body_type, T@1);

                            none ->
                                {ok, St@6}
                        end, fun(St@8) ->
                            gleam@result:'try'(case Expected_return of
                                    {some, Expected} ->
                                        unify(St@8, Body_type, Expected);

                                    none ->
                                        {ok, St@8}
                                end, fun(St@9) ->
                                    {ok, {{fn, Param_types, Body_type}, St@9}}
                                end)
                        end)
                end
            )
        end
    ).

-file("src/girard/internal/infer.gleam", 1834).
?DOC(false).
-spec check(env(), state(), glance:expression(), girard@types:type()) -> {ok,
        state()} |
    {error, girard@types:error()}.
check(Env, St, Expr, Expected) ->
    Seeded = case Expr of
        {fn, _, Params, _, _} ->
            case resolve(St, Expected) of
                {fn, Expected_params, Expected_return} ->
                    case erlang:length(Expected_params) =:= erlang:length(
                        Params
                    ) of
                        true ->
                            {ok, {Expected_params, Expected_return}};

                        false ->
                            {error, nil}
                    end;

                _ ->
                    {error, nil}
            end;

        _ ->
            {error, nil}
    end,
    case {Expr, Seeded} of
        {{fn, Span, Params@1, Return_annotation, Body}, {ok, {Seeds, Ret}}} ->
            gleam@result:'try'(
                infer_lambda(
                    Env,
                    St,
                    Params@1,
                    Return_annotation,
                    Body,
                    Seeds,
                    {some, Ret},
                    maps:new()
                ),
                fun(_use0) ->
                    {Fn_type, St@1} = _use0,
                    St@2 = record(St@1, Span, Fn_type),
                    unify(St@2, Fn_type, Expected)
                end
            );

        {_, _} ->
            gleam@result:'try'(
                infer_expr(Env, St, Expr),
                fun(_use0@1) ->
                    {T, St@3} = _use0@1,
                    unify(St@3, T, Expected)
                end
            )
    end.

-file("src/girard/internal/infer.gleam", 965).
?DOC(false).
-spec infer_bit_segment(
    env(),
    state(),
    {glance:expression(),
        list(glance:bit_string_segment_option(glance:expression()))}
) -> {ok, state()} | {error, girard@types:error()}.
infer_bit_segment(Env, St, Segment) ->
    {Value, Options} = Segment,
    Default = case Value of
        {string, _, _} ->
            girard@internal@prelude:string();

        {float, _, _} ->
            girard@internal@prelude:float();

        _ ->
            girard@internal@prelude:int()
    end,
    gleam@result:'try'(
        check(Env, St, Value, segment_value_type(Options, Default)),
        fun(St@1) ->
            gleam@list:try_fold(
                Options,
                St@1,
                fun(St@2, Option) -> case Option of
                        {size_value_option, Size} ->
                            check(
                                Env,
                                St@2,
                                Size,
                                girard@internal@prelude:int()
                            );

                        _ ->
                            {ok, St@2}
                    end end
            )
        end
    ).

-file("src/girard/internal/infer.gleam", 941).
?DOC(false).
-spec update_field(
    env(),
    state(),
    glance:record_update_field(glance:expression()),
    gleam@dict:dict(binary(), girard@types:type()),
    binary()
) -> {ok, state()} | {error, girard@types:error()}.
update_field(Env, St, Field, Label_types, Type_name) ->
    gleam@result:'try'(case erlang:element(3, Field) of
            {some, Value} ->
                infer_expr(Env, St, Value);

            none ->
                case gleam_stdlib:map_get(
                    erlang:element(2, Env),
                    erlang:element(2, Field)
                ) of
                    {ok, Scheme} ->
                        {ok, instantiate(St, Scheme)};

                    {error, _} ->
                        {error, {unbound_variable, erlang:element(2, Field)}}
                end
        end, fun(_use0) ->
            {Value_type, St@1} = _use0,
            case gleam_stdlib:map_get(Label_types, erlang:element(2, Field)) of
                {ok, Expected} ->
                    unify(St@1, Value_type, Expected);

                {error, _} ->
                    {error,
                        {no_such_field, Type_name, erlang:element(2, Field)}}
            end
        end).

-file("src/girard/internal/infer.gleam", 1135).
?DOC(false).
-spec infer_record_update(
    env(),
    state(),
    gleam@option:option(binary()),
    binary(),
    glance:expression(),
    list(glance:record_update_field(glance:expression()))
) -> {ok, {girard@types:type(), state()}} | {error, girard@types:error()}.
infer_record_update(Env, St, Module, Constructor, Record, Fields) ->
    gleam@result:'try'(
        constructor_scheme(Env, Module, Constructor),
        fun(Scheme) ->
            {Ctor_type, St@1} = instantiate(St, Scheme),
            {Field_types, Return_type} = case Ctor_type of
                {fn, Arguments, Return} ->
                    {Arguments, Return};

                Other ->
                    {[], Other}
            end,
            case Return_type of
                {named, Type_module, Type_name, Type_parameters} ->
                    Labels = constructor_field_map(Env, Module, Constructor),
                    Label_types = gleam@list:fold(
                        gleam@list:zip(Labels, Field_types),
                        maps:new(),
                        fun(Acc, Pair) -> case erlang:element(1, Pair) of
                                {some, Label} ->
                                    gleam@dict:insert(
                                        Acc,
                                        Label,
                                        erlang:element(2, Pair)
                                    );

                                none ->
                                    Acc
                            end end
                    ),
                    Updated = gleam@list:map(
                        Fields,
                        fun(Field) -> erlang:element(2, Field) end
                    ),
                    {Record_parameters, St@2} = fresh_n(
                        St@1,
                        erlang:length(Type_parameters)
                    ),
                    gleam@result:'try'(
                        infer_expr(Env, St@2, Record),
                        fun(_use0) ->
                            {Record_type, St@3} = _use0,
                            gleam@result:'try'(
                                unify(
                                    St@3,
                                    Record_type,
                                    {named,
                                        Type_module,
                                        Type_name,
                                        Record_parameters}
                                ),
                                fun(St@4) ->
                                    gleam@result:'try'(
                                        gleam@list:try_fold(
                                            Fields,
                                            St@4,
                                            fun(St@5, Field@1) ->
                                                update_field(
                                                    Env,
                                                    St@5,
                                                    Field@1,
                                                    Label_types,
                                                    Type_name
                                                )
                                            end
                                        ),
                                        fun(St@6) ->
                                            To_record_params = maps:from_list(
                                                gleam@list:zip(
                                                    gleam@list:filter_map(
                                                        Type_parameters,
                                                        fun var_id/1
                                                    ),
                                                    Record_parameters
                                                )
                                            ),
                                            gleam@result:'try'(
                                                gleam@list:try_fold(
                                                    maps:to_list(Label_types),
                                                    St@6,
                                                    fun(St@7, Pair@1) ->
                                                        {Label@1, Expected} = Pair@1,
                                                        case gleam@list:contains(
                                                            Updated,
                                                            Label@1
                                                        ) of
                                                            true ->
                                                                {ok, St@7};

                                                            false ->
                                                                unify(
                                                                    St@7,
                                                                    substitute(
                                                                        To_record_params,
                                                                        Expected
                                                                    ),
                                                                    Expected
                                                                )
                                                        end
                                                    end
                                                ),
                                                fun(St@8) ->
                                                    {ok, {Return_type, St@8}}
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    );

                _ ->
                    {error, not_a_record}
            end
        end
    ).

-file("src/girard/internal/infer.gleam", 1088).
?DOC(false).
-spec module_or_record(
    env(),
    state(),
    glance:expression(),
    binary(),
    {ok, girard@types:scheme()} | {error, nil}
) -> {ok, {girard@types:type(), state()}} | {error, girard@types:error()}.
module_or_record(Env, St, Container, Label, Module_access) ->
    case Module_access of
        {ok, Scheme} ->
            {ok, instantiate(St, Scheme)};

        {error, _} ->
            gleam@result:'try'(
                infer_expr(Env, St, Container),
                fun(_use0) ->
                    {Container_type, St@1} = _use0,
                    case resolve(St@1, Container_type) of
                        {named, _, _, _} = Record ->
                            gleam@result:'try'(
                                field_type(Env, St@1, Record, Label),
                                fun(_use0@1) ->
                                    {Field, St@2} = _use0@1,
                                    {ok, {Field, St@2}}
                                end
                            );

                        {var, _} ->
                            {Field@1, St@3} = fresh(St@1),
                            St@4 = {state,
                                erlang:element(2, St@3),
                                erlang:element(3, St@3),
                                erlang:element(4, St@3),
                                [{pending_field, Container_type, Label, Field@1} |
                                    erlang:element(5, St@3)],
                                erlang:element(6, St@3)},
                            {ok, {Field@1, St@4}};

                        _ ->
                            {error, not_a_record}
                    end
                end
            )
    end.

-file("src/girard/internal/infer.gleam", 1044).
?DOC(false).
-spec value_field(
    env(),
    state(),
    glance:expression(),
    binary(),
    {ok, girard@types:scheme()} | {error, nil}
) -> {ok, {girard@types:type(), state()}} | {error, girard@types:error()}.
value_field(Env, St, Container, Label, Module_access) ->
    gleam@result:'try'(
        infer_expr(Env, St, Container),
        fun(_use0) ->
            {Container_type, St@1} = _use0,
            case resolve(St@1, Container_type) of
                {named, _, _, _} = Record ->
                    case accessor(Env, Record, Label) of
                        {ok, _} ->
                            field_type(Env, St@1, Record, Label);

                        {error, Field_error} ->
                            case Module_access of
                                {ok, Scheme} ->
                                    {ok, instantiate(St@1, Scheme)};

                                {error, _} ->
                                    {error, Field_error}
                            end
                    end;

                {var, _} ->
                    case Module_access of
                        {ok, Scheme@1} ->
                            {ok, instantiate(St@1, Scheme@1)};

                        {error, _} ->
                            {Field, St@2} = fresh(St@1),
                            St@3 = {state,
                                erlang:element(2, St@2),
                                erlang:element(3, St@2),
                                erlang:element(4, St@2),
                                [{pending_field, Container_type, Label, Field} |
                                    erlang:element(5, St@2)],
                                erlang:element(6, St@2)},
                            {ok, {Field, St@3}}
                    end;

                _ ->
                    case Module_access of
                        {ok, Scheme@2} ->
                            {ok, instantiate(St@1, Scheme@2)};

                        {error, _} ->
                            {error, not_a_record}
                    end
            end
        end
    ).

-file("src/girard/internal/infer.gleam", 993).
?DOC(false).
-spec infer_field_access(env(), state(), glance:expression(), binary()) -> {ok,
        {girard@types:type(), state()}} |
    {error, girard@types:error()}.
infer_field_access(Env, St, Container, Label) ->
    Variant_field = case Container of
        {variable, _, Name} ->
            case gleam_stdlib:map_get(erlang:element(9, Env), Name) of
                {ok, Fields} ->
                    gleam_stdlib:map_get(Fields, Label);

                {error, _} ->
                    {error, nil}
            end;

        _ ->
            {error, nil}
    end,
    Module_access = case Container of
        {variable, _, Name@1} ->
            case gleam_stdlib:map_get(erlang:element(11, Env), Name@1) of
                {ok, Interface} ->
                    gleam_stdlib:map_get(erlang:element(3, Interface), Label);

                {error, _} ->
                    {error, nil}
            end;

        _ ->
            {error, nil}
    end,
    case Variant_field of
        {ok, Field} ->
            {ok, {Field, St}};

        {error, _} ->
            case Container of
                {variable, _, Name@2} ->
                    case gleam@dict:has_key(erlang:element(2, Env), Name@2) of
                        true ->
                            value_field(
                                Env,
                                St,
                                Container,
                                Label,
                                Module_access
                            );

                        false ->
                            module_or_record(
                                Env,
                                St,
                                Container,
                                Label,
                                Module_access
                            )
                    end;

                _ ->
                    module_or_record(Env, St, Container, Label, Module_access)
            end
    end.

-file("src/girard/internal/infer.gleam", 2119).
?DOC(false).
-spec infer_clause(
    env(),
    state(),
    glance:clause(),
    list(glance:expression()),
    list(girard@types:type()),
    girard@types:type()
) -> {ok, state()} | {error, girard@types:error()}.
infer_clause(Env, St, Clause, Subjects, Subject_types, Result) ->
    gleam@list:try_fold(
        erlang:element(2, Clause),
        St,
        fun(St@1, Patterns) ->
            gleam@result:'try'(
                gleam@list:try_fold(
                    gleam@list:zip(
                        Patterns,
                        gleam@list:zip(Subjects, Subject_types)
                    ),
                    {Env, St@1},
                    fun(Acc, Pair) ->
                        {Env@1, St@2} = Acc,
                        {Pattern, {Subject, Subject_type}} = Pair,
                        gleam@result:'try'(
                            infer_pattern(Env@1, St@2, Pattern, Subject_type),
                            fun(_use0) ->
                                {Env@2, St@3} = _use0,
                                case {Subject, Pattern} of
                                    {{variable, _, Name},
                                        {pattern_variant,
                                            _,
                                            Module,
                                            Constructor,
                                            _,
                                            _}} ->
                                        {ok,
                                            record_variant(
                                                Env@2,
                                                St@3,
                                                Module,
                                                Constructor,
                                                Subject_type,
                                                Name
                                            )};

                                    {_, _} ->
                                        {ok, {Env@2, St@3}}
                                end
                            end
                        )
                    end
                ),
                fun(_use0@1) ->
                    {Clause_env, St@4} = _use0@1,
                    gleam@result:'try'(case erlang:element(3, Clause) of
                            {some, Guard} ->
                                check(
                                    Clause_env,
                                    St@4,
                                    Guard,
                                    girard@internal@prelude:bool()
                                );

                            none ->
                                {ok, St@4}
                        end, fun(St@5) ->
                            gleam@result:'try'(
                                infer_expr(
                                    Clause_env,
                                    St@5,
                                    erlang:element(4, Clause)
                                ),
                                fun(_use0@2) ->
                                    {Body_type, St@6} = _use0@2,
                                    unify(St@6, Body_type, Result)
                                end
                            )
                        end)
                end
            )
        end
    ).

-file("src/girard/internal/infer.gleam", 2103).
?DOC(false).
-spec infer_case(
    env(),
    state(),
    list(glance:expression()),
    list(glance:clause())
) -> {ok, {girard@types:type(), state()}} | {error, girard@types:error()}.
infer_case(Env, St, Subjects, Clauses) ->
    gleam@result:'try'(
        infer_each(Env, St, Subjects),
        fun(_use0) ->
            {Subject_types, St@1} = _use0,
            {Result, St@2} = fresh(St@1),
            gleam@result:'try'(
                gleam@list:try_fold(
                    Clauses,
                    St@2,
                    fun(St@3, Clause) ->
                        infer_clause(
                            Env,
                            St@3,
                            Clause,
                            Subjects,
                            Subject_types,
                            Result
                        )
                    end
                ),
                fun(St@4) -> {ok, {Result, St@4}} end
            )
        end
    ).

-file("src/girard/internal/infer.gleam", 1405).
?DOC(false).
-spec infer_callee(env(), state(), glance:expression()) -> {ok,
        {girard@types:type(), state()}} |
    {error, girard@types:error()}.
infer_callee(Env, St, Function) ->
    case Function of
        {field_access, _, {variable, _, Name}, Label} ->
            Module_export = case gleam_stdlib:map_get(
                erlang:element(11, Env),
                Name
            ) of
                {ok, Interface} ->
                    gleam_stdlib:map_get(erlang:element(3, Interface), Label);

                {error, _} ->
                    {error, nil}
            end,
            case {Module_export, field_is_callable(Env, St, Name, Label)} of
                {{ok, Scheme}, false} ->
                    {Type_, St@1} = instantiate(St, Scheme),
                    {ok, {Type_, record(St@1, span(Function), Type_)}};

                {_, _} ->
                    infer_expr(Env, St, Function)
            end;

        _ ->
            infer_expr(Env, St, Function)
    end.

-file("src/girard/internal/infer.gleam", 1463).
?DOC(false).
-spec infer_call(
    env(),
    state(),
    glance:span(),
    glance:expression(),
    list(glance:field(glance:expression()))
) -> {ok, {girard@types:type(), state()}} | {error, girard@types:error()}.
infer_call(Env, St, Span, Function, Arguments) ->
    gleam@result:'try'(
        infer_callee(Env, St, Function),
        fun(_use0) ->
            {Fn_type, St@1} = _use0,
            gleam@result:'try'(
                order_fields(
                    Env,
                    Function,
                    Arguments,
                    fun(Label, Location) -> {variable, Location, Label} end
                ),
                fun(Ordered) ->
                    {Arg_holes, St@2} = fresh_n(St@1, erlang:length(Ordered)),
                    {Result, St@3} = fresh(St@2),
                    gleam@result:'try'(
                        unify(St@3, Fn_type, {fn, Arg_holes, Result}),
                        fun(St@4) ->
                            gleam@result:'try'(
                                gleam@list:try_fold(
                                    gleam@list:zip(Ordered, Arg_holes),
                                    St@4,
                                    fun(St@5, Pair) ->
                                        check(
                                            Env,
                                            St@5,
                                            erlang:element(1, Pair),
                                            erlang:element(2, Pair)
                                        )
                                    end
                                ),
                                fun(St@6) ->
                                    {ok, {Result, record(St@6, Span, Result)}}
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/girard/internal/infer.gleam", 1781).
?DOC(false).
-spec infer_pipe(
    env(),
    state(),
    glance:span(),
    glance:expression(),
    glance:expression()
) -> {ok, {girard@types:type(), state()}} | {error, girard@types:error()}.
infer_pipe(Env, St, Span, Left, Right) ->
    case Right of
        {call, Call_span, Function, Arguments} ->
            gleam@result:'try'(
                infer_expr(Env, St, Function),
                fun(_use0) ->
                    {Ft, St@1} = _use0,
                    Saturated = case resolve(St@1, Ft) of
                        {fn, Params, _} ->
                            erlang:length(Params) =:= erlang:length(Arguments);

                        _ ->
                            false
                    end,
                    case Saturated of
                        true ->
                            gleam@result:'try'(
                                infer_call(
                                    Env,
                                    St@1,
                                    Call_span,
                                    Function,
                                    Arguments
                                ),
                                fun(_use0@1) ->
                                    {Call_type, St@2} = _use0@1,
                                    gleam@result:'try'(
                                        infer_expr(Env, St@2, Left),
                                        fun(_use0@2) ->
                                            {Lt, St@3} = _use0@2,
                                            {Result, St@4} = fresh(St@3),
                                            gleam@result:'try'(
                                                unify(
                                                    St@4,
                                                    Call_type,
                                                    {fn, [Lt], Result}
                                                ),
                                                fun(St@5) ->
                                                    {ok,
                                                        {Result,
                                                            record(
                                                                St@5,
                                                                Span,
                                                                Result
                                                            )}}
                                                end
                                            )
                                        end
                                    )
                                end
                            );

                        false ->
                            infer_call(
                                Env,
                                St@1,
                                Call_span,
                                Function,
                                [{unlabelled_field, Left} | Arguments]
                            )
                    end
                end
            );

        _ ->
            gleam@result:'try'(
                infer_expr(Env, St, Left),
                fun(_use0@3) ->
                    {Lt@1, St@6} = _use0@3,
                    gleam@result:'try'(
                        infer_expr(Env, St@6, Right),
                        fun(_use0@4) ->
                            {Ft@1, St@7} = _use0@4,
                            {Result@1, St@8} = fresh(St@7),
                            gleam@result:'try'(
                                unify(St@8, Ft@1, {fn, [Lt@1], Result@1}),
                                fun(St@9) ->
                                    {ok,
                                        {Result@1, record(St@9, Span, Result@1)}}
                                end
                            )
                        end
                    )
                end
            )
    end.

-file("src/girard/internal/infer.gleam", 1721).
?DOC(false).
-spec infer_binop(
    env(),
    state(),
    glance:span(),
    glance:binary_operator(),
    glance:expression(),
    glance:expression()
) -> {ok, {girard@types:type(), state()}} | {error, girard@types:error()}.
infer_binop(Env, St, Span, Op, Left, Right) ->
    case Op of
        pipe ->
            infer_pipe(Env, St, Span, Left, Right);

        'and' ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:bool()),
                fun(St@1) ->
                    gleam@result:'try'(
                        check(Env, St@1, Right, girard@internal@prelude:bool()),
                        fun(St@2) ->
                            {ok, {girard@internal@prelude:bool(), St@2}}
                        end
                    )
                end
            );

        'or' ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:bool()),
                fun(St@1) ->
                    gleam@result:'try'(
                        check(Env, St@1, Right, girard@internal@prelude:bool()),
                        fun(St@2) ->
                            {ok, {girard@internal@prelude:bool(), St@2}}
                        end
                    )
                end
            );

        eq ->
            gleam@result:'try'(
                infer_expr(Env, St, Left),
                fun(_use0) ->
                    {Lt, St@3} = _use0,
                    gleam@result:'try'(
                        infer_expr(Env, St@3, Right),
                        fun(_use0@1) ->
                            {Rt, St@4} = _use0@1,
                            gleam@result:'try'(
                                unify(St@4, Lt, Rt),
                                fun(St@5) ->
                                    {ok, {girard@internal@prelude:bool(), St@5}}
                                end
                            )
                        end
                    )
                end
            );

        not_eq ->
            gleam@result:'try'(
                infer_expr(Env, St, Left),
                fun(_use0) ->
                    {Lt, St@3} = _use0,
                    gleam@result:'try'(
                        infer_expr(Env, St@3, Right),
                        fun(_use0@1) ->
                            {Rt, St@4} = _use0@1,
                            gleam@result:'try'(
                                unify(St@4, Lt, Rt),
                                fun(St@5) ->
                                    {ok, {girard@internal@prelude:bool(), St@5}}
                                end
                            )
                        end
                    )
                end
            );

        concatenate ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:string()),
                fun(St@6) ->
                    gleam@result:'try'(
                        check(
                            Env,
                            St@6,
                            Right,
                            girard@internal@prelude:string()
                        ),
                        fun(St@7) ->
                            {ok, {girard@internal@prelude:string(), St@7}}
                        end
                    )
                end
            );

        add_int ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:int()),
                fun(St@8) ->
                    gleam@result:'try'(
                        check(Env, St@8, Right, girard@internal@prelude:int()),
                        fun(St@9) ->
                            {ok, {girard@internal@prelude:int(), St@9}}
                        end
                    )
                end
            );

        sub_int ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:int()),
                fun(St@8) ->
                    gleam@result:'try'(
                        check(Env, St@8, Right, girard@internal@prelude:int()),
                        fun(St@9) ->
                            {ok, {girard@internal@prelude:int(), St@9}}
                        end
                    )
                end
            );

        mult_int ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:int()),
                fun(St@8) ->
                    gleam@result:'try'(
                        check(Env, St@8, Right, girard@internal@prelude:int()),
                        fun(St@9) ->
                            {ok, {girard@internal@prelude:int(), St@9}}
                        end
                    )
                end
            );

        div_int ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:int()),
                fun(St@8) ->
                    gleam@result:'try'(
                        check(Env, St@8, Right, girard@internal@prelude:int()),
                        fun(St@9) ->
                            {ok, {girard@internal@prelude:int(), St@9}}
                        end
                    )
                end
            );

        remainder_int ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:int()),
                fun(St@8) ->
                    gleam@result:'try'(
                        check(Env, St@8, Right, girard@internal@prelude:int()),
                        fun(St@9) ->
                            {ok, {girard@internal@prelude:int(), St@9}}
                        end
                    )
                end
            );

        add_float ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:float()),
                fun(St@10) ->
                    gleam@result:'try'(
                        check(
                            Env,
                            St@10,
                            Right,
                            girard@internal@prelude:float()
                        ),
                        fun(St@11) ->
                            {ok, {girard@internal@prelude:float(), St@11}}
                        end
                    )
                end
            );

        sub_float ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:float()),
                fun(St@10) ->
                    gleam@result:'try'(
                        check(
                            Env,
                            St@10,
                            Right,
                            girard@internal@prelude:float()
                        ),
                        fun(St@11) ->
                            {ok, {girard@internal@prelude:float(), St@11}}
                        end
                    )
                end
            );

        mult_float ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:float()),
                fun(St@10) ->
                    gleam@result:'try'(
                        check(
                            Env,
                            St@10,
                            Right,
                            girard@internal@prelude:float()
                        ),
                        fun(St@11) ->
                            {ok, {girard@internal@prelude:float(), St@11}}
                        end
                    )
                end
            );

        div_float ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:float()),
                fun(St@10) ->
                    gleam@result:'try'(
                        check(
                            Env,
                            St@10,
                            Right,
                            girard@internal@prelude:float()
                        ),
                        fun(St@11) ->
                            {ok, {girard@internal@prelude:float(), St@11}}
                        end
                    )
                end
            );

        lt_int ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:int()),
                fun(St@12) ->
                    gleam@result:'try'(
                        check(Env, St@12, Right, girard@internal@prelude:int()),
                        fun(St@13) ->
                            {ok, {girard@internal@prelude:bool(), St@13}}
                        end
                    )
                end
            );

        lt_eq_int ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:int()),
                fun(St@12) ->
                    gleam@result:'try'(
                        check(Env, St@12, Right, girard@internal@prelude:int()),
                        fun(St@13) ->
                            {ok, {girard@internal@prelude:bool(), St@13}}
                        end
                    )
                end
            );

        gt_int ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:int()),
                fun(St@12) ->
                    gleam@result:'try'(
                        check(Env, St@12, Right, girard@internal@prelude:int()),
                        fun(St@13) ->
                            {ok, {girard@internal@prelude:bool(), St@13}}
                        end
                    )
                end
            );

        gt_eq_int ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:int()),
                fun(St@12) ->
                    gleam@result:'try'(
                        check(Env, St@12, Right, girard@internal@prelude:int()),
                        fun(St@13) ->
                            {ok, {girard@internal@prelude:bool(), St@13}}
                        end
                    )
                end
            );

        lt_float ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:float()),
                fun(St@14) ->
                    gleam@result:'try'(
                        check(
                            Env,
                            St@14,
                            Right,
                            girard@internal@prelude:float()
                        ),
                        fun(St@15) ->
                            {ok, {girard@internal@prelude:bool(), St@15}}
                        end
                    )
                end
            );

        lt_eq_float ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:float()),
                fun(St@14) ->
                    gleam@result:'try'(
                        check(
                            Env,
                            St@14,
                            Right,
                            girard@internal@prelude:float()
                        ),
                        fun(St@15) ->
                            {ok, {girard@internal@prelude:bool(), St@15}}
                        end
                    )
                end
            );

        gt_float ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:float()),
                fun(St@14) ->
                    gleam@result:'try'(
                        check(
                            Env,
                            St@14,
                            Right,
                            girard@internal@prelude:float()
                        ),
                        fun(St@15) ->
                            {ok, {girard@internal@prelude:bool(), St@15}}
                        end
                    )
                end
            );

        gt_eq_float ->
            gleam@result:'try'(
                check(Env, St, Left, girard@internal@prelude:float()),
                fun(St@14) ->
                    gleam@result:'try'(
                        check(
                            Env,
                            St@14,
                            Right,
                            girard@internal@prelude:float()
                        ),
                        fun(St@15) ->
                            {ok, {girard@internal@prelude:bool(), St@15}}
                        end
                    )
                end
            )
    end.

-file("src/girard/internal/infer.gleam", 1690).
?DOC(false).
-spec infer_fields_typed(
    env(),
    state(),
    list(glance:field(glance:expression()))
) -> {ok, {list(glance:field(girard@types:type())), state()}} |
    {error, girard@types:error()}.
infer_fields_typed(Env, St, Fields) ->
    gleam@result:'try'(
        gleam@list:try_fold(
            Fields,
            {[], St},
            fun(Acc, Field) ->
                {Typed, St@1} = Acc,
                case Field of
                    {unlabelled_field, Item} ->
                        gleam@result:'try'(
                            infer_expr(Env, St@1, Item),
                            fun(_use0) ->
                                {T, St@2} = _use0,
                                {ok, {[{unlabelled_field, T} | Typed], St@2}}
                            end
                        );

                    {labelled_field, Label, Location, Item@1} ->
                        gleam@result:'try'(
                            infer_expr(Env, St@1, Item@1),
                            fun(_use0@1) ->
                                {T@1, St@3} = _use0@1,
                                {ok,
                                    {[{labelled_field, Label, Location, T@1} |
                                            Typed],
                                        St@3}}
                            end
                        );

                    {shorthand_field, Label@1, Location@1} ->
                        gleam@result:'try'(
                            infer_expr(
                                Env,
                                St@1,
                                {variable, Location@1, Label@1}
                            ),
                            fun(_use0@2) ->
                                {T@2, St@4} = _use0@2,
                                {ok,
                                    {[{labelled_field, Label@1, Location@1, T@2} |
                                            Typed],
                                        St@4}}
                            end
                        )
                end
            end
        ),
        fun(_use0@3) ->
            {Rev, St@5} = _use0@3,
            {ok, {lists:reverse(Rev), St@5}}
        end
    ).

-file("src/girard/internal/infer.gleam", 1654).
?DOC(false).
-spec infer_capture(
    env(),
    state(),
    glance:span(),
    gleam@option:option(binary()),
    glance:expression(),
    list(glance:field(glance:expression())),
    list(glance:field(glance:expression()))
) -> {ok, {girard@types:type(), state()}} | {error, girard@types:error()}.
infer_capture(Env, St, Span, Label, Function, Before, After) ->
    {Hole, St@1} = fresh(St),
    gleam@result:'try'(
        infer_expr(Env, St@1, Function),
        fun(_use0) ->
            {Fn_type, St@2} = _use0,
            gleam@result:'try'(
                infer_fields_typed(Env, St@2, Before),
                fun(_use0@1) ->
                    {Before_typed, St@3} = _use0@1,
                    gleam@result:'try'(
                        infer_fields_typed(Env, St@3, After),
                        fun(_use0@2) ->
                            {After_typed, St@4} = _use0@2,
                            Hole_field = case Label of
                                {some, Name} ->
                                    {labelled_field, Name, Span, Hole};

                                none ->
                                    {unlabelled_field, Hole}
                            end,
                            Fields = lists:append(
                                [Before_typed, [Hole_field], After_typed]
                            ),
                            gleam@result:'try'(
                                order_fields(
                                    Env,
                                    Function,
                                    Fields,
                                    fun(_, _) -> Hole end
                                ),
                                fun(Arg_types) ->
                                    {Result, St@5} = fresh(St@4),
                                    gleam@result:'try'(
                                        unify(
                                            St@5,
                                            Fn_type,
                                            {fn, Arg_types, Result}
                                        ),
                                        fun(St@6) ->
                                            Captured = {fn, [Hole], Result},
                                            {ok,
                                                {Captured,
                                                    record(St@6, Span, Captured)}}
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/girard/internal/infer.gleam", 1320).
?DOC(false).
-spec infer_fn(
    env(),
    state(),
    list(glance:fn_parameter()),
    gleam@option:option(glance:type()),
    list(glance:statement())
) -> {ok, {girard@types:type(), state()}} | {error, girard@types:error()}.
infer_fn(Env, St, Params, Return_annotation, Body) ->
    {Names@1, St@3} = gleam@list:fold(
        gleam@list:unique(fn_annotation_var_names(Params, Return_annotation)),
        {maps:new(), St},
        fun(Acc, Nm) ->
            {Names, St@1} = Acc,
            {Id, St@2} = fresh_id(St@1),
            {gleam@dict:insert(Names, Nm, {var, Id}), St@2}
        end
    ),
    {Seeds, St@4} = fresh_n(St@3, erlang:length(Params)),
    infer_lambda(
        Env,
        St@4,
        Params,
        Return_annotation,
        Body,
        Seeds,
        none,
        Names@1
    ).

-file("src/girard/internal/infer.gleam", 828).
?DOC(false).
-spec infer_expr_inner(env(), state(), glance:expression()) -> {ok,
        {girard@types:type(), state()}} |
    {error, girard@types:error()}.
infer_expr_inner(Env, St, Expr) ->
    case Expr of
        {int, _, _} ->
            {ok, {girard@internal@prelude:int(), St}};

        {float, _, _} ->
            {ok, {girard@internal@prelude:float(), St}};

        {string, _, _} ->
            {ok, {girard@internal@prelude:string(), St}};

        {variable, _, Name} ->
            case gleam_stdlib:map_get(erlang:element(2, Env), Name) of
                {ok, Scheme} ->
                    {ok, instantiate_in(Env, St, Name, Scheme)};

                {error, _} ->
                    {error, {unbound_variable, Name}}
            end;

        {negate_int, _, Value} ->
            gleam@result:'try'(
                infer_expr(Env, St, Value),
                fun(_use0) ->
                    {T, St@1} = _use0,
                    gleam@result:'try'(
                        unify(St@1, T, girard@internal@prelude:int()),
                        fun(St@2) ->
                            {ok, {girard@internal@prelude:int(), St@2}}
                        end
                    )
                end
            );

        {negate_bool, _, Value@1} ->
            gleam@result:'try'(
                infer_expr(Env, St, Value@1),
                fun(_use0@1) ->
                    {T@1, St@3} = _use0@1,
                    gleam@result:'try'(
                        unify(St@3, T@1, girard@internal@prelude:bool()),
                        fun(St@4) ->
                            {ok, {girard@internal@prelude:bool(), St@4}}
                        end
                    )
                end
            );

        {tuple, _, Elements} ->
            gleam@result:'try'(
                infer_each(Env, St, Elements),
                fun(_use0@2) ->
                    {Elem_types, St@5} = _use0@2,
                    {ok, {{tuple, Elem_types}, St@5}}
                end
            );

        {list, _, Elements@1, Rest} ->
            {Elem, St@6} = fresh(St),
            gleam@result:'try'(
                gleam@list:try_fold(
                    Elements@1,
                    St@6,
                    fun(St@7, E) ->
                        gleam@result:'try'(
                            infer_expr(Env, St@7, E),
                            fun(_use0@3) ->
                                {T@2, St@8} = _use0@3,
                                unify(St@8, T@2, Elem)
                            end
                        )
                    end
                ),
                fun(St@9) -> gleam@result:'try'(case Rest of
                            {some, R} ->
                                gleam@result:'try'(
                                    infer_expr(Env, St@9, R),
                                    fun(_use0@4) ->
                                        {T@3, St@10} = _use0@4,
                                        unify(
                                            St@10,
                                            T@3,
                                            girard@internal@prelude:list(Elem)
                                        )
                                    end
                                );

                            none ->
                                {ok, St@9}
                        end, fun(St@11) ->
                            {ok, {girard@internal@prelude:list(Elem), St@11}}
                        end) end
            );

        {fn, _, Params, Return_annotation, Body} ->
            infer_fn(Env, St, Params, Return_annotation, Body);

        {call, Span, Function, Arguments} ->
            infer_call(Env, St, Span, Function, Arguments);

        {fn_capture, Span@1, Label, Function@1, Before, After} ->
            infer_capture(Env, St, Span@1, Label, Function@1, Before, After);

        {binary_operator, Span@2, Op, Left, Right} ->
            infer_binop(Env, St, Span@2, Op, Left, Right);

        {block, _, Statements} ->
            infer_statements(Env, St, Statements);

        {'case', _, Subjects, Clauses} ->
            infer_case(Env, St, Subjects, Clauses);

        {tuple_index, _, Tuple, Index} ->
            gleam@result:'try'(
                infer_expr(Env, St, Tuple),
                fun(_use0@5) ->
                    {T@4, St@12} = _use0@5,
                    case resolve(St@12, T@4) of
                        {tuple, Elements@2} ->
                            case list_at(Elements@2, Index) of
                                {ok, Element} ->
                                    {ok, {Element, St@12}};

                                {error, _} ->
                                    {error, {tuple_index_out_of_range, Index}}
                            end;

                        {var, _} ->
                            {Element@1, St@13} = fresh(St@12),
                            St@14 = {state,
                                erlang:element(2, St@13),
                                erlang:element(3, St@13),
                                erlang:element(4, St@13),
                                [{pending_index, T@4, Index, Element@1} |
                                    erlang:element(5, St@13)],
                                erlang:element(6, St@13)},
                            {ok, {Element@1, St@14}};

                        _ ->
                            {error, not_a_tuple}
                    end
                end
            );

        {todo, _, _} ->
            {ok, fresh(St)};

        {panic, _, _} ->
            {ok, fresh(St)};

        {echo, _, Expression, _} ->
            case Expression of
                {some, E@1} ->
                    infer_expr(Env, St, E@1);

                none ->
                    {ok, {girard@internal@prelude:nil(), St}}
            end;

        {field_access, _, Container, Label@1} ->
            infer_field_access(Env, St, Container, Label@1);

        {record_update, _, Module, Constructor, Record, Fields} ->
            infer_record_update(Env, St, Module, Constructor, Record, Fields);

        {bit_string, _, Segments} ->
            gleam@result:'try'(
                gleam@list:try_fold(
                    Segments,
                    St,
                    fun(St@15, Segment) ->
                        infer_bit_segment(Env, St@15, Segment)
                    end
                ),
                fun(St@16) ->
                    {ok, {girard@internal@prelude:bit_array(), St@16}}
                end
            )
    end.

-file("src/girard/internal/infer.gleam", 819).
?DOC(false).
-spec infer_expr(env(), state(), glance:expression()) -> {ok,
        {girard@types:type(), state()}} |
    {error, girard@types:error()}.
infer_expr(Env, St, Expr) ->
    gleam@result:'try'(
        infer_expr_inner(Env, St, Expr),
        fun(_use0) ->
            {Type_, St@1} = _use0,
            {ok, {Type_, record(St@1, span(Expr), Type_)}}
        end
    ).

-file("src/girard/internal/infer.gleam", 2601).
?DOC(false).
-spec function_annotation_var_names(glance:function_()) -> list(binary()).
function_annotation_var_names(Function) ->
    From_params = gleam@list:flat_map(
        erlang:element(5, Function),
        fun(P) -> case erlang:element(4, P) of
                {some, T} ->
                    type_var_names(T);

                none ->
                    []
            end end
    ),
    case erlang:element(6, Function) of
        {some, T@1} ->
            lists:append(From_params, type_var_names(T@1));

        none ->
            From_params
    end.

-file("src/girard/internal/infer.gleam", 2597).
?DOC(false).
-spec has_annotation_vars(glance:function_()) -> boolean().
has_annotation_vars(Function) ->
    function_annotation_var_names(Function) /= [].

-file("src/girard/internal/infer.gleam", 2620).
?DOC(false).
-spec signature_skeleton(env(), state(), glance:function_()) -> {list(girard@types:type()),
    girard@types:type(),
    list(integer()),
    state()}.
signature_skeleton(Env, St, Function) ->
    {Names@1, Rigid_ids, St@3} = gleam@list:fold(
        gleam@list:unique(function_annotation_var_names(Function)),
        {maps:new(), [], St},
        fun(Acc, Name) ->
            {Names, Ids, St@1} = Acc,
            {Id, St@2} = fresh_id(St@1),
            {gleam@dict:insert(Names, Name, {var, Id}), [Id | Ids], St@2}
        end
    ),
    St@4 = mark_rigid(St@3, Rigid_ids),
    {Rev_param_types, St@7} = gleam@list:fold(
        erlang:element(5, Function),
        {[], St@4},
        fun(Acc@1, Param) ->
            {Types_, St@5} = Acc@1,
            {T, St@6} = case erlang:element(4, Param) of
                {some, Ann} ->
                    hydrate_in(Env, Names@1, St@5, Ann);

                none ->
                    fresh(St@5)
            end,
            {[T | Types_], St@6}
        end
    ),
    {Return_type, St@8} = case erlang:element(6, Function) of
        {some, Ann@1} ->
            hydrate_in(Env, Names@1, St@7, Ann@1);

        none ->
            fresh(St@7)
    end,
    {lists:reverse(Rev_param_types), Return_type, Rigid_ids, St@8}.

-file("src/girard/internal/infer.gleam", 2655).
?DOC(false).
-spec bind_params(env(), glance:function_(), list(girard@types:type())) -> env().
bind_params(Env, Function, Param_types) ->
    gleam@list:fold(
        gleam@list:zip(erlang:element(5, Function), Param_types),
        Env,
        fun(Env@1, Pair) ->
            {Param, T} = Pair,
            case erlang:element(3, Param) of
                {named, Name} ->
                    bind_value(Env@1, Name, {scheme, [], T});

                {discarded, _} ->
                    Env@1
            end
        end
    ).

-file("src/girard/internal/infer.gleam", 2671).
?DOC(false).
-spec check_body(env(), state(), glance:function_(), girard@types:type()) -> {ok,
        state()} |
    {error, girard@types:error()}.
check_body(Env, St, Function, Return_type) ->
    case erlang:element(7, Function) of
        [] ->
            {ok, St};

        _ ->
            gleam@result:'try'(
                infer_statements(Env, St, erlang:element(7, Function)),
                fun(_use0) ->
                    {Body_type, St@1} = _use0,
                    unify(St@1, Body_type, Return_type)
                end
            )
    end.

-file("src/girard/internal/infer.gleam", 2694).
?DOC(false).
-spec rigid_scheme(
    list(integer()),
    list(girard@types:type()),
    girard@types:type()
) -> girard@types:scheme().
rigid_scheme(Rigid_ids, Param_types, Return_type) ->
    {scheme, Rigid_ids, {fn, Param_types, Return_type}}.

-file("src/girard/internal/infer.gleam", 2704).
?DOC(false).
-spec function_scheme(
    env(),
    state(),
    list(girard@types:type()),
    girard@types:type()
) -> girard@types:scheme().
function_scheme(Env, St, Param_types, Return_type) ->
    generalize(St, Env, {fn, Param_types, Return_type}).

-file("src/girard/internal/infer.gleam", 2718).
?DOC(false).
-spec rigid_self_scheme(list(girard@types:type()), girard@types:type()) -> girard@types:scheme().
rigid_self_scheme(Param_types, Return_type) ->
    {scheme, [], {fn, Param_types, Return_type}}.

-file("src/girard/internal/infer.gleam", 3031).
?DOC(false).
-spec hydrate_threaded(
    env(),
    gleam@dict:dict(binary(), girard@types:type()),
    state(),
    glance:type()
) -> {girard@types:type(),
    state(),
    gleam@dict:dict(binary(), girard@types:type())}.
hydrate_threaded(Env, Names, St, Ast) ->
    {{T, St@1}, Names@1} = hydrate_with(Env, Names, St, Ast),
    {T, St@1, Names@1}.

-file("src/girard/internal/infer.gleam", 2723).
?DOC(false).
-spec infer_function(env(), state(), glance:function_()) -> {ok,
        {girard@types:type(), state()}} |
    {error, girard@types:error()}.
infer_function(Env, St, Function) ->
    {Rev_param_types, Body_env, St@4, Names@2} = gleam@list:fold(
        erlang:element(5, Function),
        {[], Env, St, maps:new()},
        fun(Acc, Param) ->
            {Types_, Env@1, St@1, Names} = Acc,
            {T@1, St@3, Names@1} = case erlang:element(4, Param) of
                {some, Ann} ->
                    hydrate_threaded(Env@1, Names, St@1, Ann);

                none ->
                    {T, St@2} = fresh(St@1),
                    {T, St@2, Names}
            end,
            Env@2 = case erlang:element(3, Param) of
                {named, Name} ->
                    bind_value(Env@1, Name, {scheme, [], T@1});

                {discarded, _} ->
                    Env@1
            end,
            {[T@1 | Types_], Env@2, St@3, Names@1}
        end
    ),
    Param_types = lists:reverse(Rev_param_types),
    case erlang:element(7, Function) of
        [] ->
            {Return_type, St@6} = case erlang:element(6, Function) of
                {some, Ann@1} ->
                    {T@2, St@5, _} = hydrate_threaded(Env, Names@2, St@4, Ann@1),
                    {T@2, St@5};

                none ->
                    fresh(St@4)
            end,
            {ok, {{fn, Param_types, Return_type}, St@6}};

        _ ->
            gleam@result:'try'(
                infer_statements(Body_env, St@4, erlang:element(7, Function)),
                fun(_use0) ->
                    {Body_type, St@7} = _use0,
                    gleam@result:'try'(case erlang:element(6, Function) of
                            {some, Ann@2} ->
                                {T@3, St@8, _} = hydrate_threaded(
                                    Env,
                                    Names@2,
                                    St@7,
                                    Ann@2
                                ),
                                unify(St@8, Body_type, T@3);

                            none ->
                                {ok, St@7}
                        end, fun(St@9) ->
                            {ok, {{fn, Param_types, Body_type}, St@9}}
                        end)
                end
            )
    end.

-file("src/girard/internal/infer.gleam", 2780).
?DOC(false).
-spec infer_constant(env(), state(), glance:constant()) -> {ok,
        {girard@types:type(), state()}} |
    {error, girard@types:error()}.
infer_constant(Env, St, Constant) ->
    gleam@result:'try'(
        infer_expr(Env, St, erlang:element(6, Constant)),
        fun(_use0) ->
            {Value_type, St@1} = _use0,
            case erlang:element(5, Constant) of
                {some, Ann} ->
                    {T, St@2} = hydrate(Env, St@1, Ann),
                    gleam@result:'try'(
                        unify(St@2, Value_type, T),
                        fun(St@3) -> {ok, {Value_type, St@3}} end
                    );

                none ->
                    {ok, {Value_type, St@1}}
            end
        end
    ).

-file("src/girard/internal/infer.gleam", 2867).
?DOC(false).
-spec shared_accessors(
    list(gleam@dict:dict(binary(), girard@types:type())),
    list(integer()),
    girard@types:type()
) -> gleam@dict:dict(binary(), girard@types:scheme()).
shared_accessors(Variants, Param_ids, Return_type) ->
    case Variants of
        [] ->
            maps:new();

        [First | Rest] ->
            gleam@dict:fold(
                First,
                maps:new(),
                fun(Accessors, Label, Field_type) ->
                    Shared = gleam@list:all(
                        Rest,
                        fun(Variant) ->
                            gleam_stdlib:map_get(Variant, Label) =:= {ok,
                                Field_type}
                        end
                    ),
                    case Shared of
                        true ->
                            gleam@dict:insert(
                                Accessors,
                                Label,
                                {scheme,
                                    Param_ids,
                                    {fn, [Return_type], Field_type}}
                            );

                        false ->
                            Accessors
                    end
                end
            )
    end.

-file("src/girard/internal/infer.gleam", 3012).
?DOC(false).
-spec variant_field_type(glance:variant_field()) -> glance:type().
variant_field_type(Field) ->
    case Field of
        {labelled_variant_field, Item, _} ->
            Item;

        {unlabelled_variant_field, Item@1} ->
            Item@1
    end.

-file("src/girard/internal/infer.gleam", 2798).
?DOC(false).
-spec register_custom_type(env(), state(), glance:custom_type()) -> {env(),
    state()}.
register_custom_type(Env, St, Custom_type) ->
    Env@1 = declare_type(
        Env,
        erlang:element(3, Custom_type),
        erlang:length(erlang:element(6, Custom_type))
    ),
    {Rev_param_ids, St@3} = gleam@list:fold(
        erlang:element(6, Custom_type),
        {[], St},
        fun(Acc, _) ->
            {Ids, St@1} = Acc,
            {Id, St@2} = fresh_id(St@1),
            {[Id | Ids], St@2}
        end
    ),
    Param_ids = lists:reverse(Rev_param_ids),
    Param_vars = gleam@list:map(Param_ids, fun(Field@0) -> {var, Field@0} end),
    Names = maps:from_list(
        gleam@list:zip(erlang:element(6, Custom_type), Param_vars)
    ),
    Return_type = {named,
        erlang:element(10, Env@1),
        erlang:element(3, Custom_type),
        Param_vars},
    {Env@5, St@8, Rev_variant_labels} = gleam@list:fold(
        erlang:element(7, Custom_type),
        {Env@1, St@3, []},
        fun(Acc@1, Variant) ->
            {Env@2, St@4, Variant_labels} = Acc@1,
            {Rev_field_types, Labelled@2, St@7} = gleam@list:fold(
                erlang:element(3, Variant),
                {[], maps:new(), St@4},
                fun(Acc@2, Field) ->
                    {Types_, Labelled, St@5} = Acc@2,
                    {T, St@6} = hydrate_in(
                        Env@2,
                        Names,
                        St@5,
                        variant_field_type(Field)
                    ),
                    Labelled@1 = case Field of
                        {labelled_variant_field, _, Label} ->
                            gleam@dict:insert(Labelled, Label, T);

                        {unlabelled_variant_field, _} ->
                            Labelled
                    end,
                    {[T | Types_], Labelled@1, St@6}
                end
            ),
            Field_types = lists:reverse(Rev_field_types),
            Ctor_type = case Field_types of
                [] ->
                    Return_type;

                _ ->
                    {fn, Field_types, Return_type}
            end,
            Env@3 = register_field_map(
                Env@2,
                erlang:element(2, Variant),
                gleam@list:map(erlang:element(3, Variant), fun(F) -> case F of
                            {labelled_variant_field, _, Label@1} ->
                                {some, Label@1};

                            {unlabelled_variant_field, _} ->
                                none
                        end end)
            ),
            Env@4 = bind_value(
                Env@3,
                erlang:element(2, Variant),
                {scheme, Param_ids, Ctor_type}
            ),
            {Env@4, St@7, [Labelled@2 | Variant_labels]}
        end
    ),
    Accessors = shared_accessors(
        lists:reverse(Rev_variant_labels),
        Param_ids,
        Return_type
    ),
    Env@6 = {env,
        erlang:element(2, Env@5),
        erlang:element(3, Env@5),
        erlang:element(4, Env@5),
        erlang:element(5, Env@5),
        gleam@dict:insert(
            erlang:element(6, Env@5),
            erlang:element(3, Custom_type),
            Accessors
        ),
        erlang:element(7, Env@5),
        erlang:element(8, Env@5),
        erlang:element(9, Env@5),
        erlang:element(10, Env@5),
        erlang:element(11, Env@5),
        erlang:element(12, Env@5),
        erlang:element(13, Env@5)},
    {Env@6, St@8}.

-file("src/girard/internal/infer.gleam", 2979).
?DOC(false).
-spec resolve_one(env(), state(), pending()) -> {ok, {state(), boolean()}} |
    {error, girard@types:error()}.
resolve_one(Env, St, Item) ->
    case Item of
        {pending_field, Container, Label, Field} ->
            case resolve(St, Container) of
                {named, _, _, _} = Record ->
                    gleam@result:'try'(
                        accessor(Env, Record, Label),
                        fun(Scheme) ->
                            {Accessor_type, St@1} = instantiate(St, Scheme),
                            gleam@result:'try'(
                                unify(
                                    St@1,
                                    Accessor_type,
                                    {fn, [Container], Field}
                                ),
                                fun(St@2) -> {ok, {St@2, true}} end
                            )
                        end
                    );

                {var, _} ->
                    {ok, {St, false}};

                _ ->
                    {error, not_a_record}
            end;

        {pending_index, Container@1, Index, Result} ->
            case resolve(St, Container@1) of
                {tuple, Elements} ->
                    case list_at(Elements, Index) of
                        {ok, Element} ->
                            gleam@result:'try'(
                                unify(St, Element, Result),
                                fun(St@3) -> {ok, {St@3, true}} end
                            );

                        {error, _} ->
                            {error, {tuple_index_out_of_range, Index}}
                    end;

                {var, _} ->
                    {ok, {St, false}};

                _ ->
                    {error, not_a_tuple}
            end
    end.

-file("src/girard/internal/infer.gleam", 2948).
?DOC(false).
-spec resolve_pending_loop(env(), state(), list(pending()), integer()) -> {ok,
        state()} |
    {error, girard@types:error()}.
resolve_pending_loop(Env, St, Pending, Fuel) ->
    case {Pending, Fuel =< 0} of
        {[], _} ->
            {ok, St};

        {[_ | _], true} ->
            {error, not_a_record};

        {_, false} ->
            gleam@result:'try'(
                gleam@list:try_fold(
                    Pending,
                    {St, [], false},
                    fun(Acc, Item) ->
                        {St@1, Remaining, Progressed} = Acc,
                        gleam@result:'try'(
                            resolve_one(Env, St@1, Item),
                            fun(_use0) ->
                                {St@2, Resolved} = _use0,
                                case Resolved of
                                    true ->
                                        {ok, {St@2, Remaining, true}};

                                    false ->
                                        {ok,
                                            {St@2,
                                                [Item | Remaining],
                                                Progressed}}
                                end
                            end
                        )
                    end
                ),
                fun(_use0@1) ->
                    {St@3, Remaining@1, Progressed@1} = _use0@1,
                    case Progressed@1 of
                        true ->
                            resolve_pending_loop(
                                Env,
                                St@3,
                                lists:reverse(Remaining@1),
                                Fuel - 1
                            );

                        false ->
                            {error, not_a_record}
                    end
                end
            )
    end.

-file("src/girard/internal/infer.gleam", 2935).
?DOC(false).
-spec resolve_pending(env(), state()) -> {ok, state()} |
    {error, girard@types:error()}.
resolve_pending(Env, St) ->
    Pending = lists:reverse(erlang:element(5, St)),
    resolve_pending_loop(
        Env,
        {state,
            erlang:element(2, St),
            erlang:element(3, St),
            erlang:element(4, St),
            [],
            erlang:element(6, St)},
        Pending,
        erlang:length(Pending)
    ).