Module: Jade::Frontend::TypeChecking::Inference::Implementation

Extended by:
Helpers, Implementation
Included in:
Implementation
Defined in:
lib/jade/frontend/type_checking/inference/implementation.rb

Instance Method Summary collapse

Methods included from Helpers

check, generalize, instantiate, type_from_symbol, unify

Instance Method Details

#infer(node, registry, state, _expected) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/jade/frontend/type_checking/inference/implementation.rb', line 9

def infer(node, registry, state, _expected)
  node => AST::Implementation(functions:)

  impl = node.symbol

  interface_sym   = registry.lookup(impl.interface)
  interface_qname = impl.interface.qualified_name

  # Build the concrete type for the constructor, respecting its type params
  concrete_type, = type_from_symbol(impl.type, registry, state.env.var_gen)

  functions
    .reduce(state) do |st, impl_fn|
      impl_fn => AST::ImplementationFunction(name:)

      iface_fn = interface_sym
        .functions
        .find { |f| f.name == name }

      # Instantiate the interface function with fresh vars
      iface_fn_type, iface_fn_constraints =
        type_from_symbol(iface_fn, registry, st.env.var_gen)

      # The type var standing for the interface's type param
      t_var = iface_fn_constraints
        .find { |c| c.interface == interface_qname }
        .type

      # When the interface uses t_var as a constructor (e.g. f(a) -> f(b)),
      # bind it to a partial application so that f(a) beta-reduces correctly.
      # For 1-param types (Maybe): tail = [] → constructor
      # For 2-param types (Result): tail = [e] → PartialApplication[Constructor, [e]]
      binding_type =
        case [constructor_var_in?(iface_fn_type, t_var.id), concrete_type]
        in [true, Type::Application(constructor:, args:)]
          tail = args.drop(1)
          tail.empty? ? constructor : Type::PartialApplication[constructor, tail]
        else
          concrete_type
        end

      # Bind the interface type var to the concrete type
      st_after_bind = st.unify(t_var, binding_type) { nil }
      expected_type = st_after_bind.env.substitution.apply(iface_fn_type)

      infer_fn(
        impl_fn,
        registry,
        st_after_bind,
        Expected.check(expected_type),
        interface_qname,
      )
    end
    .then { [it, Result.init(Type.unit)] }
end