jade-lang 0.11.3 → 0.12.1

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +112 -0
  3. data/lib/jade/ast.rb +1 -1
  4. data/lib/jade/codegen/context.rb +18 -1
  5. data/lib/jade/codegen/function_call.rb +9 -2
  6. data/lib/jade/codegen/inlines.rb +2 -0
  7. data/lib/jade/codegen.rb +7 -5
  8. data/lib/jade/diagnostics/renderer.rb +5 -1
  9. data/lib/jade/entry.rb +32 -10
  10. data/lib/jade/frontend/forward_declaration/error/ambiguous_type.rb +36 -0
  11. data/lib/jade/frontend/forward_declaration/error.rb +1 -0
  12. data/lib/jade/frontend/forward_declaration/helper.rb +7 -0
  13. data/lib/jade/frontend/forward_declaration/import_declaration.rb +10 -2
  14. data/lib/jade/frontend/semantic_analysis/constructor_reference.rb +5 -0
  15. data/lib/jade/frontend/semantic_analysis/error/ambiguous_name.rb +36 -0
  16. data/lib/jade/frontend/semantic_analysis/error/top_level_statement.rb +28 -0
  17. data/lib/jade/frontend/semantic_analysis/error.rb +2 -0
  18. data/lib/jade/frontend/semantic_analysis/module_node.rb +21 -1
  19. data/lib/jade/frontend/semantic_analysis/variable_reference.rb +5 -0
  20. data/lib/jade/frontend/semantic_analysis.rb +4 -6
  21. data/lib/jade/frontend/type_checking/constraints/deriving/show.rb +24 -0
  22. data/lib/jade/frontend/type_checking/error/narrowed_method_variable.rb +36 -0
  23. data/lib/jade/frontend/type_checking/error/narrowed_signature.rb +53 -0
  24. data/lib/jade/frontend/type_checking/error.rb +2 -0
  25. data/lib/jade/frontend/type_checking/inference/assign.rb +7 -4
  26. data/lib/jade/frontend/type_checking/inference/case_of.rb +1 -6
  27. data/lib/jade/frontend/type_checking/inference/constructor_reference.rb +1 -1
  28. data/lib/jade/frontend/type_checking/inference/function_declaration.rb +21 -0
  29. data/lib/jade/frontend/type_checking/inference/helpers.rb +11 -0
  30. data/lib/jade/frontend/type_checking/inference/implementation.rb +23 -2
  31. data/lib/jade/frontend/type_checking/inference/lambda.rb +1 -4
  32. data/lib/jade/frontend/type_checking/inference/list.rb +16 -2
  33. data/lib/jade/frontend/type_checking/narrowing.rb +93 -0
  34. data/lib/jade/frontend/type_checking/pattern_checks.rb +30 -0
  35. data/lib/jade/frontend/type_checking/state.rb +9 -2
  36. data/lib/jade/frontend/type_checking.rb +3 -6
  37. data/lib/jade/frontend.rb +0 -22
  38. data/lib/jade/parsing/error.rb +15 -0
  39. data/lib/jade/registry.rb +1 -1
  40. data/lib/jade/stdlib/compiled.rb +8 -1
  41. data/lib/jade/stdlib/dict.rb +12 -0
  42. data/lib/jade/stdlib/intrinsics.rb +8 -1
  43. data/lib/jade/stdlib/set.rb +12 -0
  44. data/lib/jade/stdlib.rb +8 -1
  45. data/lib/jade/symbol/ambiguous.rb +5 -0
  46. data/lib/jade/symbol.rb +1 -0
  47. data/lib/jade/version.rb +1 -1
  48. metadata +10 -2
@@ -0,0 +1,53 @@
1
+ module Jade
2
+ module Frontend
3
+ module TypeChecking
4
+ module Error
5
+ class NarrowedSignature < Jade::Error
6
+ def initialize(entry, span, function_name:, var:, found:)
7
+ @function_name = function_name
8
+ @var = var
9
+ @found = found
10
+ super(entry:, span:)
11
+ end
12
+
13
+ def message
14
+ case @found
15
+ in Type::Var
16
+ "`#{@function_name}` says `#{@var}` and `#{@found}` can be different " \
17
+ 'types, but its body needs them to be the same'
18
+ else
19
+ "`#{@function_name}` says it works for any `#{@var}`, but its body only " \
20
+ "works when `#{@var}` is #{shown}"
21
+ end
22
+ end
23
+
24
+ def label
25
+ 'narrower than its signature'
26
+ end
27
+
28
+ def notes
29
+ [Jade::Diagnostics::Annotation[
30
+ :help,
31
+ 'write the type the body needs in the signature, or make the body ' \
32
+ 'work for any type',
33
+ ]]
34
+ end
35
+
36
+ private
37
+
38
+ def shown
39
+ kept, renamed = @found
40
+ .unbound_vars
41
+ .uniq(&:id)
42
+ .partition { it.name && it.name != @var }
43
+
44
+ renamed
45
+ .zip(('a'..'z').to_a - [@var, *kept.map(&:name)])
46
+ .to_h { |(var, letter)| [var.id, Type.var(var.id, letter)] }
47
+ .then { Substitution[it].apply(@found).to_s }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -7,6 +7,7 @@ require 'jade/frontend/type_checking/error/case_of_branches_type_mismatch'
7
7
  require 'jade/frontend/type_checking/error/derivation_failed'
8
8
  require 'jade/frontend/type_checking/error/recursive_derivation'
9
9
  require 'jade/frontend/type_checking/error/function_body_type_mismatch'
10
+ require 'jade/frontend/type_checking/error/narrowed_signature'
10
11
  require 'jade/frontend/type_checking/error/function_call_type_mismatch'
11
12
  require 'jade/frontend/type_checking/error/if_branch_type_mismatch'
12
13
  require 'jade/frontend/type_checking/error/if_branches_type_mismatch'
@@ -24,5 +25,6 @@ require 'jade/frontend/type_checking/error/unresolved_constraint'
24
25
  require 'jade/frontend/type_checking/error/implementation_type_mismatch'
25
26
  require 'jade/frontend/type_checking/error/implementation_function_constraint'
26
27
  require 'jade/frontend/type_checking/error/cross_module_requirement'
28
+ require 'jade/frontend/type_checking/error/narrowed_method_variable'
27
29
  require 'jade/frontend/type_checking/error/undeclared_requirement'
28
30
  require 'jade/frontend/type_checking/error/missing_implementation'
@@ -33,10 +33,13 @@ module Jade
33
33
  [pattern_state, expr_result.constraints]
34
34
  end
35
35
 
36
- PatternAnalysis::Exhaustiveness
37
- .assert([pattern], pattern.range, final_state.env, expr_result.type)
38
- .then { final_state.add_errors(it) }
39
- .unify_result(expr_result.with(constraints: residual_cs), expected.type)
36
+ final_state
37
+ .defer_patterns([pattern], pattern.range, expr_result.type)
38
+ .unify_result(
39
+ expr_result.with(constraints: residual_cs),
40
+ expected.type,
41
+ &type_mismatch(state, node)
42
+ )
40
43
  end
41
44
 
42
45
  private
@@ -86,12 +86,7 @@ module Jade
86
86
  patterns = branches.map(&:pattern)
87
87
  type = result.apply(state.env.substitution).type
88
88
 
89
- [
90
- PatternAnalysis::Exhaustiveness.assert(patterns, node.range, state.env, type),
91
- PatternAnalysis::Redundancy.assert(patterns, state.env, type),
92
- ]
93
- .flatten
94
- .then { state.add_errors(it) }
89
+ state.defer_patterns(patterns, node.range, type)
95
90
  end
96
91
 
97
92
  def mistyped_patterns?(node, state)
@@ -12,7 +12,7 @@ module Jade
12
12
  state
13
13
  .env
14
14
  .lookup(symbol.qualified_name)
15
- .then { state.unify_result(it, expected.type) }
15
+ .then { state.unify_result(it, expected.type, &type_mismatch(state, node)) }
16
16
  end
17
17
  end
18
18
  end
@@ -41,6 +41,7 @@ module Jade
41
41
  function_name: node.name,
42
42
  )
43
43
  end
44
+ .then { |st| report_narrowing(st, node, symbol, registry, fn_type) }
44
45
  .then do |st|
45
46
  next st if st.env.bindings[symbol.qualified_name].is_a?(Scheme) && !st.skip_constraints
46
47
 
@@ -58,6 +59,26 @@ module Jade
58
59
  end
59
60
  .then { [it, Result.init(Type.unit)] }
60
61
  end
62
+
63
+ private
64
+
65
+ def report_narrowing(state, node, symbol, registry, fn_type)
66
+ Type
67
+ .from_symbol(registry.lookup(symbol), registry, state.env.var_gen)
68
+ .first
69
+ .then { Narrowing.against_declaration(it, state.env.substitution.apply(fn_type)) }
70
+ .then { it ? state.add_errors([narrowed(it, state, node)]) : state }
71
+ end
72
+
73
+ def narrowed((var, found), state, node)
74
+ Error::NarrowedSignature.new(
75
+ state.env.entry_name,
76
+ node.range,
77
+ function_name: node.name,
78
+ var: var.name,
79
+ found:,
80
+ )
81
+ end
61
82
  end
62
83
  end
63
84
  end
@@ -36,6 +36,17 @@ module Jade
36
36
 
37
37
  result
38
38
  end
39
+
40
+ def type_mismatch(state, node)
41
+ ->(error) do
42
+ Error::TypeMismatch.new(
43
+ state.env.entry_name,
44
+ node.range,
45
+ expected: error.expected,
46
+ actual: error.actual,
47
+ )
48
+ end
49
+ end
39
50
  end
40
51
  end
41
52
  end
@@ -58,7 +58,7 @@ module Jade
58
58
  Expected.check(expected_type),
59
59
  interface_qname,
60
60
  concrete_type,
61
- iface_fn_type,
61
+ [iface_fn_type, t_var],
62
62
  )
63
63
  end
64
64
  .then { [it, Result.init(Type.unit)] }
@@ -151,11 +151,19 @@ module Jade
151
151
  end
152
152
  end
153
153
 
154
- def record_requirements(state, constraints, head_type, sig_type, interface_qname, impl_fn, fn_name)
154
+ def record_requirements(state, constraints, head_type, signature, interface_qname, impl_fn, fn_name)
155
+ signature => [sig_type, t_var]
155
156
  substitution = state.env.substitution
156
157
  head_vars = substitution.apply(head_type).unbound_vars.map(&:id).to_set
157
158
  names = signature_names(sig_type, substitution)
158
159
 
160
+ narrowed = Narrowing.against_method(sig_type, t_var, head_vars, substitution)
161
+ unless narrowed.empty?
162
+ return state.add_errors(
163
+ narrowed.map { narrowing(state, impl_fn, interface_qname, fn_name, it) },
164
+ )
165
+ end
166
+
159
167
  free = constraints
160
168
  .map { substitution.apply(it) }
161
169
  .select { it.type.is_a?(Type::Var) && !head_vars.include?(it.type.id) }
@@ -171,6 +179,19 @@ module Jade
171
179
  )
172
180
  end
173
181
 
182
+ def narrowing(state, impl_fn, interface_qname, fn_name, narrowed)
183
+ narrowed => [var_name, bound_to]
184
+
185
+ Error::NarrowedMethodVariable.new(
186
+ state.env.entry_name,
187
+ impl_fn.range,
188
+ interface: interface_qname,
189
+ fn_name:,
190
+ var_name:,
191
+ bound_to: bound_to.to_s,
192
+ )
193
+ end
194
+
174
195
  def signature_names(sig_type, substitution)
175
196
  sig_type
176
197
  .unbound_vars
@@ -54,10 +54,7 @@ module Jade
54
54
  in AST::Pattern::Binding | AST::Pattern::Wildcard
55
55
  acc
56
56
  else
57
- concrete_t = body_state.env.substitution.apply(t)
58
- PatternAnalysis::Exhaustiveness
59
- .assert([p], p.range, acc.env, concrete_t)
60
- .then { acc.add_errors(it) }
57
+ acc.defer_patterns([p], p.range, t)
61
58
  end
62
59
  end
63
60
 
@@ -12,7 +12,14 @@ module Jade
12
12
  if items.empty?
13
13
  Type.list.apply([state.fresh])
14
14
  .then { Result.init(it) }
15
- .then { return state.unify_result(it, expected.type, expected.rigid_vars) }
15
+ .then do
16
+ return state.unify_result(
17
+ it,
18
+ expected.type,
19
+ expected.rigid_vars,
20
+ &type_mismatch(state, node)
21
+ )
22
+ end
16
23
  end
17
24
 
18
25
  head, *rest = items
@@ -29,7 +36,14 @@ module Jade
29
36
 
30
37
  Result
31
38
  .init(Type.list.apply([items_result.type]), items_result.constraints)
32
- .then { items_state.unify_result(it, expected.type, expected.rigid_vars) }
39
+ .then do
40
+ items_state.unify_result(
41
+ it,
42
+ expected.type,
43
+ expected.rigid_vars,
44
+ &type_mismatch(items_state, node)
45
+ )
46
+ end
33
47
  end
34
48
 
35
49
  private
@@ -0,0 +1,93 @@
1
+ module Jade
2
+ module Frontend
3
+ module TypeChecking
4
+ # Unification is symmetric, so pinning a variable a declaration
5
+ # quantifies simply binds it, and a call site instantiating that
6
+ # declaration afresh never sees that it did. Two places can do it: a
7
+ # function's own body, and an implementation against the method it
8
+ # implements.
9
+ module Narrowing
10
+ extend self
11
+
12
+ # Against a fresh copy of the declaration, since a narrowing collected
13
+ # in an earlier round is already part of the generalised type and the
14
+ # substitution no longer shows it.
15
+ def against_declaration(declared, actual)
16
+ pairs(declared, actual).then do |found|
17
+ found.find { |(_, type)| !type.is_a?(Type::Var) } || merged(found)
18
+ end
19
+ end
20
+
21
+
22
+ # An implementation is the other place a quantified variable can be
23
+ # pinned: the method promises a variable each call site settles, and
24
+ # unification will happily bind it to the implementation's own.
25
+ #
26
+ # The interface's own parameter is exempt — binding that to the
27
+ # implemented type is what an implementation is.
28
+ def against_method(sig_type, t_var, head_vars, substitution)
29
+ sig_type
30
+ .unbound_vars
31
+ .reject { it.id == t_var.id }
32
+ .filter_map { pinned(it, substitution.apply(it), head_vars) }
33
+ .uniq(&:first)
34
+ end
35
+
36
+ private
37
+
38
+ # Pinned to one of the implemented type's own variables is the common
39
+ # case, and naming it reads as nonsense when the two are spelled alike.
40
+ def pinned(var, applied, head_vars)
41
+ case applied
42
+ in Type::Var(id:) if !head_vars.include?(id) then nil
43
+ in Type::Var then [var.name, 'the type this implements']
44
+ else [var.name, "`#{applied}`"]
45
+ end
46
+ end
47
+
48
+ def merged(found)
49
+ found
50
+ .uniq { |(var, _)| var.id }
51
+ .group_by { |(_, type)| type.id }
52
+ .values
53
+ .find { it.size > 1 }
54
+ &.then { |((var, _), (other, _))| [var, other] }
55
+ end
56
+
57
+ def pairs(declared, actual)
58
+ case declared
59
+ in Type::Var
60
+ [[declared, actual]]
61
+
62
+ in Type::Application(constructor:, args:)
63
+ pairs(constructor, actual.constructor) + zipped(args, actual.args)
64
+
65
+ in Type::Function(args:, return_type:)
66
+ zipped(args, actual.args) + pairs(return_type, actual.return_type)
67
+
68
+ in Type::AnonymousRecord(fields:, row_var:)
69
+ fields.flat_map { |name, type| pairs(type, actual.fields.fetch(name)) } +
70
+ row_pairs(row_var, fields, actual)
71
+
72
+ else
73
+ []
74
+ end
75
+ end
76
+
77
+ def zipped(declared, actual)
78
+ declared
79
+ .zip(actual)
80
+ .flat_map { |(d, a)| pairs(d, a) }
81
+ end
82
+
83
+ def row_pairs(row_var, fields, actual)
84
+ return [] unless row_var
85
+
86
+ (actual.fields.keys - fields.keys).empty? && actual.row_var ?
87
+ pairs(row_var, actual.row_var) :
88
+ [[row_var, actual]]
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,30 @@
1
+ module Jade
2
+ module Frontend
3
+ module TypeChecking
4
+ # The coverage and redundancy of every `case` in a definition, decided
5
+ # against the substitution inference ended with rather than the one in
6
+ # scope where the patterns were written.
7
+ module PatternChecks
8
+ extend self
9
+
10
+ def run(state)
11
+ state
12
+ .pattern_checks
13
+ .flat_map { |(patterns, range, type)| errors_for(patterns, range, type, state.env) }
14
+ .then { state.add_errors(it) }
15
+ end
16
+
17
+ private
18
+
19
+ def errors_for(patterns, range, type, env)
20
+ resolved = env.substitution.apply(type)
21
+
22
+ [
23
+ PatternAnalysis::Exhaustiveness.assert(patterns, range, env, resolved),
24
+ PatternAnalysis::Redundancy.assert(patterns, env, resolved),
25
+ ].flatten
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,9 +1,16 @@
1
1
  module Jade
2
2
  module Frontend
3
3
  module TypeChecking
4
- State = Data.define(:env, :errors, :skip_constraints, :impl_requirements) do
4
+ State = Data.define(:env, :errors, :skip_constraints, :impl_requirements, :pattern_checks) do
5
5
  def self.init(env, skip_constraints: false)
6
- new(env, [], skip_constraints, {})
6
+ new(env, [], skip_constraints, {}, [])
7
+ end
8
+
9
+ # Coverage is decided once inference is done: a pattern in a lambda is
10
+ # checked before the call that gives the lambda's parameter its type,
11
+ # so the subject can still be a variable here.
12
+ def defer_patterns(patterns, range, type)
13
+ with(pattern_checks: pattern_checks + [[patterns, range, type]])
7
14
  end
8
15
 
9
16
  def require_impl(key, constraints)
@@ -7,6 +7,8 @@ require 'jade/frontend/type_checking/error'
7
7
  require 'jade/frontend/type_checking/expected'
8
8
  require 'jade/frontend/type_checking/inference'
9
9
  require 'jade/frontend/type_checking/loader'
10
+ require 'jade/frontend/type_checking/narrowing'
11
+ require 'jade/frontend/type_checking/pattern_checks'
10
12
  require 'jade/frontend/type_checking/port_resolution'
11
13
  require 'jade/frontend/type_checking/requirements'
12
14
  require 'jade/frontend/type_checking/result'
@@ -27,7 +29,7 @@ module Jade
27
29
  .load(entry, registry)
28
30
  .then { collect_constraints(entry, registry, it) }
29
31
  .then { check_node(entry.ast, registry, State.init(it), Expected.infer(it.fresh)) }
30
- .then { |state, _| Requirements.reconcile(entry, registry, state) }
32
+ .then { |state, _| Requirements.reconcile(entry, registry, PatternChecks.run(state)) }
31
33
  .then { |amended, state| complete(amended, state, registry) }
32
34
  .and_then { PortResolution.resolve(it, registry) }
33
35
  end
@@ -126,11 +128,6 @@ module Jade
126
128
  .is_a?(Symbol::InteropFunction)
127
129
  end
128
130
 
129
- def check_repl(node, registry, env = Env.new)
130
- check_node(node, registry, env, Expected.infer(env.fresh))
131
- .to_result
132
- end
133
-
134
131
  def check_node(node, registry, state, expected_type)
135
132
  case node
136
133
  in AST::Body then Inference::Body
data/lib/jade/frontend.rb CHANGED
@@ -46,28 +46,6 @@ module Jade
46
46
  end
47
47
  end
48
48
 
49
- def run_repl(ast, registry, current_entry, scope, env, var_gen)
50
- registry ||= registry_with_basics
51
- current_entry ||= entry_with_basics('JadeRepl')
52
- scope ||= SemanticAnalysis::Scope.new
53
- env ||= TypeChecking::Env.new
54
- var_gen ||= TypeChecking::VarGen.new
55
-
56
- ForwardDeclaration
57
- .declare(ast, registry, current_entry)
58
- .then { |entry| FixityFixer.fix(ast).then { [it, entry] } }
59
- .then do |fixed_ast, updated_entry|
60
- updated_registry = registry.update_module(updated_entry)
61
- SemanticAnalysis
62
- .analyze_repl(fixed_ast, updated_registry, scope, updated_entry)
63
- .and_then do |(enhanced_ast, new_scope)|
64
- enhanced_ast = Desugaring.desugar_resolved(enhanced_ast, updated_registry)
65
- TypeChecking.check_repl(enhanced_ast, updated_registry, env, var_gen)
66
- .map { |type, new_env| [enhanced_ast, type, updated_registry, updated_entry, new_scope, new_env] }
67
- end
68
- end
69
- end
70
-
71
49
  def run_up_to_semantic_analysis(ast)
72
50
  registry, current_entry = entry_with_basics(ast)
73
51
 
@@ -52,6 +52,21 @@ module Jade
52
52
  def message
53
53
  "#{context_prefix}Unexpected end of input, expected #{expected}"
54
54
  end
55
+
56
+ def to_diagnostic(registry = nil, source: nil)
57
+ super.then do |diagnostic|
58
+ diagnostic.primary => { source: labelled, span: }
59
+ next diagnostic unless labelled && span.nil?
60
+
61
+ diagnostic.with(primary: diagnostic.primary.with(span: last_character(labelled.text)))
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def last_character(text)
68
+ text.b.rstrip.bytesize.then { it.zero? ? nil : (it - 1)...it }
69
+ end
55
70
  end
56
71
 
57
72
  class UnexpectedTokenError < Error
data/lib/jade/registry.rb CHANGED
@@ -75,5 +75,5 @@ module Jade
75
75
  end
76
76
  end
77
77
 
78
- ImportEntry = Data.define(:module_name, :alias, :unqualified_symbols, :qualified_symbols)
78
+ ImportEntry = Data.define(:module_name, :alias, :unqualified_symbols, :qualified_symbols, :implicit)
79
79
  end
@@ -39,7 +39,14 @@ module Jade
39
39
  def resolve_imports(entry)
40
40
  imports
41
41
  .reduce(entry) do |acc, stdlib|
42
- ImportEntry[stdlib.entry.name, stdlib.entry.name, stdlib.default_imports, stdlib.entry.exposes]
42
+ ImportEntry
43
+ .new(
44
+ module_name: stdlib.entry.name,
45
+ alias: stdlib.entry.name,
46
+ unqualified_symbols: stdlib.default_imports,
47
+ qualified_symbols: stdlib.entry.exposes,
48
+ implicit: true,
49
+ )
43
50
  .then { acc.import(it) }
44
51
  end
45
52
  end
@@ -69,6 +69,18 @@ module Jade
69
69
  end
70
70
  end
71
71
 
72
+ function(
73
+ :show_with,
74
+ { show_key: 'k -> String', show_value: 'v -> String', dict: 'Dict(k, v)' },
75
+ 'String',
76
+ private: true,
77
+ ) do |show_key, show_value, dict|
78
+ dict.hash
79
+ .map { |k, v| "#{show_key.call(k)}: #{show_value.call(v)}" }
80
+ .join(', ')
81
+ .then { "Dict(#{it})" }
82
+ end
83
+
72
84
  function(:keys, { dict: 'Dict(k, v)' }, 'List(k)')
73
85
  function(:values, { dict: 'Dict(k, v)' }, 'List(v)')
74
86
  function(:to_list, { dict: 'Dict(k, v)' }, 'List(Tuple2(k, v))')
@@ -285,7 +285,14 @@ module Jade
285
285
  # TODO: This is the same code from stdlib that auto imports stuff.
286
286
  imports
287
287
  .reduce(entry) do |acc, stdlib|
288
- ImportEntry[stdlib.entry.name, stdlib.entry.name, stdlib.default_imports, stdlib.entry.exposes]
288
+ ImportEntry
289
+ .new(
290
+ module_name: stdlib.entry.name,
291
+ alias: stdlib.entry.name,
292
+ unqualified_symbols: stdlib.default_imports,
293
+ qualified_symbols: stdlib.entry.exposes,
294
+ implicit: true,
295
+ )
289
296
  .then { acc.import(it) }
290
297
  end
291
298
  end
@@ -24,6 +24,18 @@ module Jade
24
24
  constraints: [['Basics.Eq', 'a']],
25
25
  )
26
26
 
27
+ function(
28
+ :show_with,
29
+ { show_value: 'a -> String', set: 'Set(a)' },
30
+ 'String',
31
+ private: true,
32
+ ) do |show_value, set|
33
+ set.hash.keys
34
+ .map { show_value.call(it) }
35
+ .join(', ')
36
+ .then { "Set(#{it})" }
37
+ end
38
+
27
39
  function(:"empty?", { set: 'Set(a)' }, 'Bool')
28
40
  function(:size, { set: 'Set(a)' }, 'Int')
29
41
 
data/lib/jade/stdlib.rb CHANGED
@@ -107,7 +107,14 @@ module Jade
107
107
  def add_imports(entry)
108
108
  (STDLIBS - EXTENSIONS)
109
109
  .reduce(entry) do |acc, stdlib|
110
- ImportEntry[stdlib.entry.name, stdlib.entry.name, stdlib.default_imports, stdlib.entry.exposes]
110
+ ImportEntry
111
+ .new(
112
+ module_name: stdlib.entry.name,
113
+ alias: stdlib.entry.name,
114
+ unqualified_symbols: stdlib.default_imports,
115
+ qualified_symbols: stdlib.entry.exposes,
116
+ implicit: true,
117
+ )
111
118
  .then { acc.import(it) }
112
119
  end
113
120
  end
@@ -0,0 +1,5 @@
1
+ module Jade
2
+ module Symbol
3
+ Ambiguous = Data.define(:name, :modules)
4
+ end
5
+ end
data/lib/jade/symbol.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'jade/symbol/base'
2
2
 
3
+ require 'jade/symbol/ambiguous'
3
4
  require 'jade/symbol/anonymous_record'
4
5
  require 'jade/symbol/constructor'
5
6
  require 'jade/symbol/derived_function'
data/lib/jade/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Jade
2
- VERSION = '0.11.3'
2
+ VERSION = '0.12.1'
3
3
  end