jade-lang 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +91 -1
  3. data/README.md +12 -11
  4. data/lib/jade/codegen/function_call.rb +1 -1
  5. data/lib/jade/codegen/inlines.rb +13 -0
  6. data/lib/jade/codegen/{port_decoder.rb → port_codec.rb} +27 -15
  7. data/lib/jade/codegen.rb +7 -3
  8. data/lib/jade/debug.rb +59 -0
  9. data/lib/jade/diagnostics/renderer.rb +16 -5
  10. data/lib/jade/frontend/forward_declaration/interop_import_declaration.rb +13 -3
  11. data/lib/jade/frontend/pattern_analysis/matrix.rb +57 -23
  12. data/lib/jade/frontend/semantic_analysis/constructor_reference.rb +33 -7
  13. data/lib/jade/frontend/semantic_analysis/error/constructor_not_found.rb +20 -5
  14. data/lib/jade/frontend/type_checking/constraints/deriving/decodable.rb +25 -17
  15. data/lib/jade/frontend/type_checking/constraints/deriving/encodable.rb +25 -34
  16. data/lib/jade/frontend/type_checking/constraints/deriving/eq.rb +51 -175
  17. data/lib/jade/frontend/type_checking/constraints/deriving/helpers.rb +133 -0
  18. data/lib/jade/frontend/type_checking/constraints/deriving/show.rb +186 -0
  19. data/lib/jade/frontend/type_checking/constraints/deriving.rb +2 -1
  20. data/lib/jade/frontend/type_checking/env.rb +3 -0
  21. data/lib/jade/frontend/type_checking/error/port_not_encodable.rb +38 -0
  22. data/lib/jade/frontend/type_checking/port_resolution.rb +64 -16
  23. data/lib/jade/interop/runtime.rb +10 -2
  24. data/lib/jade/module_loader.rb +27 -3
  25. data/lib/jade/parsing/error.rb +19 -0
  26. data/lib/jade/runtime.rb +1 -0
  27. data/lib/jade/stdlib/debug.rb +12 -0
  28. data/lib/jade/stdlib/decode.rb +22 -0
  29. data/lib/jade/stdlib/encode.rb +17 -0
  30. data/lib/jade/stdlib/intrinsics.rb +3 -0
  31. data/lib/jade/stdlib/show.rb +40 -0
  32. data/lib/jade/stdlib.rb +5 -2
  33. data/lib/jade/symbol/interop_function.rb +3 -1
  34. data/lib/jade/symbol.rb +2 -2
  35. data/lib/jade/version.rb +1 -1
  36. metadata +8 -3
@@ -3,21 +3,36 @@ module Jade
3
3
  module SemanticAnalysis
4
4
  module Error
5
5
  class ConstructorNotFound < Jade::Error
6
+ # `fix` is where the `(..)` is missing: `:expose` when the module
7
+ # that owns the type keeps its constructors to itself, `:import`
8
+ # when it exposes them and this module's import didn't ask for them.
9
+ Hint = Data.define(:module_name, :type_name, :fix)
10
+
6
11
  attr_reader :candidates
7
12
 
8
- def initialize(entry, span, name:, exposed_type_module: nil, candidates: [])
13
+ def initialize(entry, span, name:, hint: nil, candidates: [])
9
14
  @name = name
10
- @exposed_type_module = exposed_type_module
15
+ @hint = hint
11
16
  @candidates = candidates
12
17
  super(entry:, span:)
13
18
  end
14
19
 
15
20
  def message
16
21
  base = "I cannot find a `#{@name}` constructor"
17
- return base unless @exposed_type_module
18
22
 
19
- "#{base}. The type `#{@name}` is exposed by `#{@exposed_type_module}` but its " \
20
- "constructor is private — add `#{@name}(..)` to that module's `exposing` list."
23
+ case @hint
24
+ in nil
25
+ base
26
+
27
+ in Hint(module_name:, type_name:, fix: :expose)
28
+ "#{base}. The type `#{type_name}` is exposed by `#{module_name}` but its " \
29
+ "constructor is private — add `#{type_name}(..)` to that module's " \
30
+ "`exposing` list."
31
+
32
+ in Hint(module_name:, type_name:, fix: :import)
33
+ "#{base}. `#{module_name}` exposes it — add `#{type_name}(..)` to this " \
34
+ "module's `import #{module_name} exposing (...)`."
35
+ end
21
36
  end
22
37
 
23
38
  def label
@@ -11,13 +11,23 @@ module Jade
11
11
 
12
12
  def supports?(interface) = interface == INTERFACE
13
13
 
14
+ # One decoder combinator per structural type, taking a decoder per
15
+ # element. `Tuple2(a, b)` is `Decode.tuple(dec_a, dec_b)` the same
16
+ # way `List(a)` is `Decode.list(dec_a)`.
17
+ STRUCTURAL = {
18
+ 'List.List' => 'Decode.list',
19
+ 'Maybe.Maybe' => 'Decode.nullable',
20
+ 'Set.Set' => 'Decode.set',
21
+ 'Dict.Dict' => 'Decode.dict',
22
+ 'Tuple.Tuple2' => 'Decode.tuple',
23
+ 'Tuple.Tuple3' => 'Decode.tuple3',
24
+ 'Tuple.Tuple4' => 'Decode.tuple4',
25
+ }.freeze
26
+
14
27
  def derive(constraint, registry, entry_name, &lookup)
15
28
  case constraint.type
16
- in Type::Application(constructor: Type::Constructor(name: 'List.List'), args: [inner])
17
- derive_unary(constraint, inner, 'Decode.list', lookup, entry_name)
18
-
19
- in Type::Application(constructor: Type::Constructor(name: 'Maybe.Maybe'), args: [inner])
20
- derive_unary(constraint, inner, 'Decode.nullable', lookup, entry_name)
29
+ in Type::Application(constructor: Type::Constructor(name:), args:) if STRUCTURAL.key?(name)
30
+ derive_structural(constraint, args, STRUCTURAL.fetch(name), lookup, entry_name)
21
31
 
22
32
  in Type::Application(constructor: Type::Constructor(name:), args:)
23
33
  resolved_sym = Symbol
@@ -56,19 +66,17 @@ module Jade
56
66
  ]
57
67
  end
58
68
 
59
- def derive_unary(constraint, inner_type, stdlib_fn, lookup, entry_name)
60
- dep = Type.constraint(INTERFACE, inner_type, nil)
61
-
62
- resolve_dep(dep, lookup, entry_name).and_then do |dep_impl|
63
- body = [:call,
64
- [:stdlib_fn, stdlib_fn],
65
- [
66
- [:impl_arg, 0, 'decoder'],
67
- ],
68
- ]
69
+ def derive_structural(constraint, inner_types, stdlib_fn, lookup, entry_name)
70
+ body = [:call,
71
+ [:stdlib_fn, stdlib_fn],
72
+ inner_types.each_index.map { [:impl_arg, it, 'decoder'] },
73
+ ]
69
74
 
70
- Ok[implementation(constraint, body, [dep_impl])]
71
- end
75
+ inner_types
76
+ .map { Type.constraint(INTERFACE, it, nil) }
77
+ .map { resolve_dep(it, lookup, entry_name) }
78
+ .then { Results.sequence(it) }
79
+ .map { implementation(constraint, body, it) }
72
80
  end
73
81
 
74
82
  def derive_struct(constraint, struct_sym, type_args, registry, lookup, entry_name)
@@ -13,13 +13,23 @@ module Jade
13
13
  interface == INTERFACE
14
14
  end
15
15
 
16
+ # One encoder combinator per structural type, taking an encoder per
17
+ # element plus the value itself. The name is what the emitted
18
+ # encoder binds its argument to.
19
+ STRUCTURAL = {
20
+ 'List.List' => ['Encode.list', 'items'],
21
+ 'Maybe.Maybe' => ['Encode.nullable', 'maybe'],
22
+ 'Set.Set' => ['Encode.set', 'set'],
23
+ 'Dict.Dict' => ['Encode.dict', 'dict'],
24
+ 'Tuple.Tuple2' => ['Encode.tuple', 'pair'],
25
+ 'Tuple.Tuple3' => ['Encode.tuple3', 'triple'],
26
+ 'Tuple.Tuple4' => ['Encode.tuple4', 'quad'],
27
+ }.freeze
28
+
16
29
  def derive(constraint, registry, entry_name, &lookup)
17
30
  case constraint.type
18
- in Type::Application(constructor: Type::Constructor(name: 'List.List'), args: [inner])
19
- derive_list(constraint, inner, lookup, entry_name)
20
-
21
- in Type::Application(constructor: Type::Constructor(name: 'Maybe.Maybe'), args: [inner])
22
- derive_nullable(constraint, inner, lookup, entry_name)
31
+ in Type::Application(constructor: Type::Constructor(name:), args:) if STRUCTURAL.key?(name)
32
+ derive_structural(constraint, args, *STRUCTURAL.fetch(name), lookup, entry_name)
23
33
 
24
34
  in Type::Application(constructor: Type::Constructor(name:), args:)
25
35
  resolved_sym = Symbol
@@ -55,36 +65,17 @@ module Jade
55
65
  ]
56
66
  end
57
67
 
58
- def derive_list(constraint, inner_type, lookup, entry_name)
59
- dep = Type.constraint(INTERFACE, inner_type, nil)
60
-
61
- lookup.call(dep).and_then do |dep_impl|
62
- body = [:call,
63
- [:stdlib_fn, 'Encode.list'],
64
- [
65
- [:impl_arg, 0, 'encoder'],
66
- [:var, 'items'],
67
- ],
68
- ]
69
-
70
- Ok[implementation(constraint, params: ['items'], body:, deps: [dep_impl])]
71
- end
72
- end
73
-
74
- def derive_nullable(constraint, inner_type, lookup, entry_name)
75
- dep = Type.constraint(INTERFACE, inner_type, nil)
76
-
77
- lookup.call(dep).and_then do |dep_impl|
78
- body = [:call,
79
- [:stdlib_fn, 'Encode.nullable'],
80
- [
81
- [:impl_arg, 0, 'encoder'],
82
- [:var, 'maybe'],
83
- ],
84
- ]
68
+ def derive_structural(constraint, inner_types, stdlib_fn, param, lookup, entry_name)
69
+ body = [:call,
70
+ [:stdlib_fn, stdlib_fn],
71
+ inner_types.each_index.map { [:impl_arg, it, 'encoder'] } + [[:var, param]],
72
+ ]
85
73
 
86
- Ok[implementation(constraint, params: ['maybe'], body:, deps: [dep_impl])]
87
- end
74
+ inner_types
75
+ .map { Type.constraint(INTERFACE, it, nil) }
76
+ .map { lookup.call(it) }
77
+ .then { Results.sequence(it) }
78
+ .map { implementation(constraint, params: [param], body:, deps: it) }
88
79
  end
89
80
 
90
81
  def derive_nullary_union(constraint, union_sym, registry)
@@ -12,157 +12,56 @@ module Jade
12
12
  def supports?(interface) = interface == INTERFACE
13
13
 
14
14
  def derive(constraint, registry, entry_name, &lookup)
15
- resolve(constraint, registry, entry_name, lookup)
15
+ resolve_constraint(constraint, registry, entry_name, lookup)
16
16
  end
17
17
 
18
18
  private
19
19
 
20
- def resolve(constraint, registry, entry_name, lookup)
21
- case [constraint.interface, constraint.type]
22
- in [INTERFACE, Type::Application(constructor:, args:)]
23
- registry
24
- .implementations[[constraint.interface, constructor.name]]
25
- .then do
26
- it \
27
- ? Ok[it]
28
- : derive_for_type(constraint, constructor, args, registry, lookup, entry_name)
29
- end
30
- .on_err { return Err[it] } => Ok(impl)
20
+ def derive_union(constraint, symbol, registry, lookup, entry_name)
21
+ type_vars = symbol.type_params.map(&:name)
22
+ index_map = type_vars.each_with_index.map.to_h
23
+ variants = symbol.variants.map { registry.lookup(it) }
24
+
25
+ concrete = variants
26
+ .flat_map(&:args)
27
+ .reject { it in Symbol::Variable }
28
+ .map { instantiate(it, {}, registry) }
29
+ .uniq
31
30
 
32
- case impl
33
- in Symbol::ImplementationTemplate
34
- deps = dependencies_of(impl, args)
35
- resolved_deps = deps.filter_map { |dep|
36
- next if dep.type in Type::Var
37
- lookup.call(dep).on_err { return Err[it] } => Ok[resolved]
38
- resolved
31
+
32
+ concrete
33
+ .map { lookup.call(Type.constraint(INTERFACE, it, constraint.origin)) }
34
+ .then { Results.sequence(it) }
35
+ .and_then do |deps|
36
+ cases = variants.map {
37
+ build_variant_case(it, index_map, concrete, registry, constraint.origin)
39
38
  }
40
39
 
41
- Symbol::Implementation.new(
42
- module_name: nil,
43
- interface: Symbol.type_ref_from_qualified_name(constraint.interface),
44
- type: constraint.type,
45
- type_params: args,
46
- constraints: deps,
47
- functions: impl.functions,
48
- deps: resolved_deps,
49
- extends: [],
50
- decl_span: nil,
40
+ eq_fn = Symbol::DerivedFunction.new(
41
+ params: ["one", "other"],
42
+ body: [:case,
43
+ [:list, [[:var, "one"], [:var, "other"]]],
44
+ cases + [[[:_], [false]]],
45
+ ],
51
46
  )
52
47
 
53
- else
54
- impl
48
+ Ok[build_union_impl(constraint, type_vars, concrete, eq_fn, deps)]
55
49
  end
56
- .then { Ok[it] }
57
-
58
- in [INTERFACE, Type::AnonymousRecord(fields:)]
59
- derive_record_eq(constraint, fields, lookup)
60
-
61
- else
62
- Err[
63
- Error::DerivationFailed
64
- .new(entry_name, constraint.origin.range, constraint:, trace: [])
65
- ]
66
- end
67
- end
68
-
69
- def dependencies_of(impl, args)
70
- subst = impl
71
- .type_params
72
- .map(&:id)
73
- .zip(args)
74
- .to_h
75
-
76
- impl
77
- .constraints
78
- .map { it.with(type: substitute_type(it.type, subst)) }
79
- end
80
-
81
- def substitute_type(type, subst)
82
- case type
83
-
84
- in Type::Var(id:)
85
- subst.fetch(id, type)
86
-
87
- in Type::Application(constructor:, args:)
88
- Type::Application.new(
89
- constructor: constructor,
90
- args: args.map { substitute_type(it, subst) }
91
- )
92
-
93
- in Type::AnonymousRecord(fields:)
94
- Type::AnonymousRecord.new(
95
- fields: fields.transform_values { substitute_type(it, subst) }
96
- )
97
-
98
- else
99
- type
100
- end
101
- end
102
-
103
- def derive_for_type(constraint, constructor, args, registry, lookup, entry_name)
104
- symbol =
105
- Symbol
106
- .type_ref_from_qualified_name(constructor.name)
107
- .then { registry.lookup(it) }
108
-
109
- case symbol
110
- in Symbol::Union
111
- Ok[derive_union_eq(constraint, symbol, registry, lookup)]
112
-
113
- in Symbol::Struct
114
- derive_struct_eq(constraint, symbol, args, registry, lookup, entry_name)
115
-
116
- else
117
- Err[
118
- Error::DerivationFailed
119
- .new(entry_name, constraint.origin.range, constraint:, trace: [])
120
- ]
121
- end
122
50
  end
123
51
 
124
- def derive_union_eq(constraint, symbol, registry, lookup)
125
- type_vars = symbol.type_params.map(&:name)
126
- index_map = type_vars.each_with_index.map.to_h
127
-
128
- cases = symbol.variants
129
- .map { registry.lookup(it) }
130
- .map { build_variant_case(it, index_map, lookup, constraint.origin) }
52
+ def build_union_impl(constraint, type_vars, concrete, eq_fn, deps)
53
+ return implementation(constraint, { '(==)' => eq_fn }, deps:) if type_vars.empty?
131
54
 
132
- eq_fn = Symbol::DerivedFunction.new(
133
- params: ["one", "other"],
134
- body: [:case,
135
- [:list, [[:var, "one"], [:var, "other"]]],
136
- cases + [[[:_], [false]]],
137
- ],
55
+ Symbol::ImplementationTemplate.new(
56
+ interface: Symbol.type_ref_from_qualified_name(constraint.interface),
57
+ type: constraint.type,
58
+ type_params: type_vars.map { Type.var(it) },
59
+ constraints: union_constraints(constraint, type_vars, concrete),
60
+ functions: { '(==)' => eq_fn },
138
61
  )
139
-
140
- if type_vars.empty?
141
- Symbol::Implementation.new(
142
- module_name: nil,
143
- interface: Symbol.type_ref_from_qualified_name(constraint.interface),
144
- type: constraint.type,
145
- type_params: [],
146
- constraints: [],
147
- functions: { '(==)' => eq_fn },
148
- deps: [],
149
- extends: [],
150
- decl_span: nil,
151
- )
152
- else
153
- Symbol::ImplementationTemplate.new(
154
- interface: Symbol.type_ref_from_qualified_name(constraint.interface),
155
- type: constraint.type,
156
- type_params: type_vars.map { Type.var(it) },
157
- constraints: type_vars.map {
158
- Type.constraint(INTERFACE, Type.var(it), constraint.origin)
159
- },
160
- functions: { '(==)' => eq_fn },
161
- )
162
- end
163
62
  end
164
63
 
165
- def build_variant_case(variant, index_map, lookup, origin)
64
+ def build_variant_case(variant, index_map, concrete, registry, origin)
166
65
  field_count = variant.args.length
167
66
 
168
67
  left_vars = (0...field_count).map { |i| "l#{i}" }
@@ -173,24 +72,17 @@ module Jade
173
72
 
174
73
  comparisons =
175
74
  variant.args.each_with_index.map do |arg_type, i|
176
- case arg_type
177
-
178
- in Symbol::Variable(name:)
179
- idx = index_map[name]
180
-
181
- [:call,
182
- [:impl_arg, idx, "(==)"],
183
- [[:var, left_vars[i]], [:var, right_vars[i]]]
184
- ]
185
-
186
- else
187
- lookup.call(Type.constraint(INTERFACE, arg_type, origin)) => Ok[impl]
188
-
189
- [:call,
190
- [:impl_value, impl, "(==)"],
191
- [[:var, left_vars[i]], [:var, right_vars[i]]]
192
- ]
193
- end
75
+ idx =
76
+ case arg_type
77
+ in Symbol::Variable(name:) then index_map[name]
78
+ else
79
+ index_map.size + concrete.index(instantiate(arg_type, {}, registry))
80
+ end
81
+
82
+ [:call,
83
+ [:impl_arg, idx, "(==)"],
84
+ [[:var, left_vars[i]], [:var, right_vars[i]]]
85
+ ]
194
86
  end
195
87
 
196
88
  body =
@@ -204,7 +96,7 @@ module Jade
204
96
  [[:list, [left_pattern, right_pattern]], body]
205
97
  end
206
98
 
207
- def derive_record_eq(constraint, fields, lookup)
99
+ def derive_record(constraint, fields, lookup)
208
100
  field_types = fields.values
209
101
  field_keys = fields.keys
210
102
 
@@ -221,7 +113,7 @@ module Jade
221
113
  end
222
114
  end
223
115
 
224
- def derive_struct_eq(constraint, struct_sym, type_args, registry, lookup, entry_name)
116
+ def derive_struct(constraint, struct_sym, type_args, registry, lookup, entry_name)
225
117
  fields = struct_fields(struct_sym, type_args, registry)
226
118
  field_types = fields.map { |_, t| t }
227
119
  field_names = fields.map { |k, _| k.to_s }
@@ -236,27 +128,11 @@ module Jade
236
128
  end
237
129
  end
238
130
 
239
- def resolve_field_deps(field_types, lookup, origin)
240
- field_types
241
- .map { lookup.call(Type.constraint(INTERFACE, it, origin)) }
242
- .then { Results.sequence(it) }
243
- end
244
-
245
131
  def build_record_impl(constraint, comparisons, deps)
246
- body = comparisons.empty? ? true : comparisons.reduce { |a, b| [:and, a, b] }
247
- eq_fn = Symbol::DerivedFunction.new(params: ['one', 'other'], body:)
248
-
249
- Symbol::Implementation.new(
250
- module_name: nil,
251
- interface: Symbol.type_ref_from_qualified_name(constraint.interface),
252
- type: constraint.type,
253
- type_params: [],
254
- constraints: [],
255
- functions: { '(==)' => eq_fn },
256
- deps:,
257
- extends: [],
258
- decl_span: nil,
259
- )
132
+ comparisons
133
+ .then { it.empty? ? true : it.reduce { |a, b| [:and, a, b] } }
134
+ .then { Symbol::DerivedFunction.new(params: ['one', 'other'], body: it) }
135
+ .then { implementation(constraint, { '(==)' => it }, deps:) }
260
136
  end
261
137
  end
262
138
  end
@@ -33,6 +33,139 @@ module Jade
33
33
  .map { |name, sym| [name, instantiate(sym, subst, registry)] }
34
34
  end
35
35
 
36
+ def failed(constraint, entry_name)
37
+ Err[
38
+ Error::DerivationFailed
39
+ .new(entry_name, constraint.origin.range, constraint:, trace: [])
40
+ ]
41
+ end
42
+
43
+ def implementation(constraint, functions, deps: [], type_params: [], constraints: [])
44
+ Symbol::Implementation.new(
45
+ module_name: nil,
46
+ interface: Symbol.type_ref_from_qualified_name(constraint.interface),
47
+ type: constraint.type,
48
+ type_params:,
49
+ constraints:,
50
+ functions:,
51
+ deps:,
52
+ extends: [],
53
+ decl_span: nil,
54
+ )
55
+ end
56
+
57
+ def resolve_field_deps(field_types, lookup, origin)
58
+ field_types
59
+ .map { lookup.call(Type.constraint(self::INTERFACE, it, origin)) }
60
+ .then { Results.sequence(it) }
61
+ end
62
+
63
+ def resolve_constraint(constraint, registry, entry_name, lookup)
64
+ special = special_case(constraint, lookup)
65
+ return special if special
66
+
67
+ case constraint.type
68
+ in Type::Application(constructor:, args:)
69
+ dispatch_type(constraint, constructor, args, registry, lookup, entry_name)
70
+ .on_err { return Err[it] } => Ok(impl)
71
+
72
+ case impl
73
+ in Symbol::ImplementationTemplate
74
+ instantiate_template(constraint, impl, args, lookup)
75
+ else
76
+ Ok[impl]
77
+ end
78
+
79
+ in Type::AnonymousRecord(fields:)
80
+ derive_record(constraint, fields, lookup)
81
+
82
+ else
83
+ failed(constraint, entry_name)
84
+ end
85
+ end
86
+
87
+ def special_case(_constraint, _lookup)
88
+ nil
89
+ end
90
+
91
+ def dispatch_type(constraint, constructor, args, registry, lookup, entry_name)
92
+ existing = registry.implementations[[constraint.interface, constructor.name]]
93
+ return Ok[existing] if existing
94
+
95
+ Symbol
96
+ .type_ref_from_qualified_name(constructor.name)
97
+ .then { registry.lookup(it) }
98
+ .then do |symbol|
99
+ case symbol
100
+ in Symbol::Union
101
+ derive_union(constraint, symbol, registry, lookup, entry_name)
102
+
103
+ in Symbol::Struct
104
+ derive_struct(constraint, symbol, args, registry, lookup, entry_name)
105
+
106
+ else
107
+ failed(constraint, entry_name)
108
+ end
109
+ end
110
+ end
111
+
112
+ def instantiate_template(constraint, impl, args, lookup)
113
+ deps = dependencies_of(impl, args)
114
+ resolved = deps.filter_map { |dep|
115
+ next if dep.type in Type::Var
116
+ lookup.call(dep).on_err { return Err[it] } => Ok[found]
117
+ found
118
+ }
119
+
120
+ Ok[
121
+ implementation(
122
+ constraint, impl.functions,
123
+ deps: resolved, type_params: args, constraints: deps,
124
+ )
125
+ ]
126
+ end
127
+
128
+ def union_constraints(constraint, type_vars, concrete)
129
+ type_vars
130
+ .map { Type.var(it) }
131
+ .concat(concrete)
132
+ .map { Type.constraint(self::INTERFACE, it, constraint.origin) }
133
+ end
134
+
135
+ def dependencies_of(impl, args)
136
+ subst = impl
137
+ .type_params
138
+ .map(&:id)
139
+ .zip(args)
140
+ .to_h
141
+
142
+ impl
143
+ .constraints
144
+ .map { it.with(type: substitute_type(it.type, subst)) }
145
+ end
146
+
147
+ def substitute_type(type, subst)
148
+ case type
149
+
150
+ in Type::Var(id:)
151
+ subst.fetch(id, type)
152
+
153
+ in Type::Application(constructor:, args:)
154
+ Type::Application.new(
155
+ constructor: constructor,
156
+ args: args.map { substitute_type(it, subst) }
157
+ )
158
+
159
+ in Type::AnonymousRecord(fields:)
160
+ Type::AnonymousRecord.new(
161
+ fields: fields.transform_values { substitute_type(it, subst) }
162
+ )
163
+
164
+ else
165
+ type
166
+ end
167
+ end
168
+
36
169
  def instantiate(sym, subst, registry)
37
170
  case sym
38
171
  in Symbol::Variable(name:)