jade-lang 0.12.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/lib/jade/codegen/inlines.rb +2 -0
- data/lib/jade/frontend/type_checking/constraints/deriving/show.rb +24 -0
- data/lib/jade/frontend/type_checking/inference/assign.rb +2 -3
- data/lib/jade/frontend/type_checking/inference/case_of.rb +1 -6
- data/lib/jade/frontend/type_checking/inference/lambda.rb +1 -4
- data/lib/jade/frontend/type_checking/pattern_checks.rb +30 -0
- data/lib/jade/frontend/type_checking/state.rb +9 -2
- data/lib/jade/frontend/type_checking.rb +2 -1
- data/lib/jade/stdlib/dict.rb +12 -0
- data/lib/jade/stdlib/set.rb +12 -0
- data/lib/jade/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1b6d2de0940d8842121c48456180fa3cbc04345d0bf991c31f8e008019c4e017
|
|
4
|
+
data.tar.gz: 425323f1b3c92b1d817b088a3973b86c94f7743fe185197cb5d60c680281184f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0da5c33762fdac3b212474c72925c1d47c48b915f7b4a6877a6328b3c07ac15430e0f97c5647d714886d498c94850a1a448100ae29cfd73d7f97abc3bfc2248c
|
|
7
|
+
data.tar.gz: ee26d40ebaa14f0587675692a83d977f6e7eae0ba80c342929847636c9c5509778fa7e19977974deb2421bf7cab0e17a066cafd4188f121d6165347cd1a4b3b4
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,30 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.12.1] - 2026-09-21
|
|
10
|
+
|
|
11
|
+
Both of these make code compile that should have compiled, so they ride a
|
|
12
|
+
patch rather than waiting for a minor.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **A pattern in a lambda is checked against the type the caller gives it.**
|
|
17
|
+
Coverage was decided where the pattern was written, which for a lambda is
|
|
18
|
+
before the call that types its parameter — so the subject was still a
|
|
19
|
+
variable and anything but a binding or a wildcard was reported as
|
|
20
|
+
non-exhaustive. `(a, b) = p` inside a `List.map` is the common shape;
|
|
21
|
+
`Tuple.first` / `Tuple.second` or a one-armed `case` were the workarounds.
|
|
22
|
+
Coverage and redundancy now run once inference has finished.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **`Show` renders a `Dict` and a `Set`.** Both already had `Eq`, so they
|
|
27
|
+
compared but could not be printed, and anything that shows a value on
|
|
28
|
+
failure — a test assertion, a debug line — stopped compiling at
|
|
29
|
+
`Show cannot be derived for Dict(String, Int)`. Keys, values and elements
|
|
30
|
+
render through their own instances, so a `String` key keeps its quotes and a
|
|
31
|
+
nested list prints as a list.
|
|
32
|
+
|
|
9
33
|
## [0.12.0] - 2026-09-17
|
|
10
34
|
|
|
11
35
|
### Breaking
|
data/lib/jade/codegen/inlines.rb
CHANGED
|
@@ -24,6 +24,8 @@ module Jade
|
|
|
24
24
|
# reason. Contained because derivation is private — see
|
|
25
25
|
# vault jade/plans/jade-test-runner.md for the four layers involved.
|
|
26
26
|
LIST = 'List.List'
|
|
27
|
+
DICT = 'Dict.Dict'
|
|
28
|
+
SET = 'Set.Set'
|
|
27
29
|
|
|
28
30
|
def special_case(constraint, lookup)
|
|
29
31
|
case constraint.type
|
|
@@ -33,6 +35,12 @@ module Jade
|
|
|
33
35
|
in Type::Application(constructor: Type::Constructor(name: LIST), args: [inner])
|
|
34
36
|
list_show(constraint, inner, lookup)
|
|
35
37
|
|
|
38
|
+
in Type::Application(constructor: Type::Constructor(name: DICT), args: [key, value])
|
|
39
|
+
container_show(constraint, [key, value], 'Dict.show_with', lookup)
|
|
40
|
+
|
|
41
|
+
in Type::Application(constructor: Type::Constructor(name: SET), args: [inner])
|
|
42
|
+
container_show(constraint, [inner], 'Set.show_with', lookup)
|
|
43
|
+
|
|
36
44
|
else
|
|
37
45
|
nil
|
|
38
46
|
end
|
|
@@ -57,6 +65,22 @@ module Jade
|
|
|
57
65
|
end
|
|
58
66
|
end
|
|
59
67
|
|
|
68
|
+
# Threads each element's own `show` into the stdlib function that
|
|
69
|
+
# renders the container.
|
|
70
|
+
def container_show(constraint, inners, fn, lookup)
|
|
71
|
+
inners
|
|
72
|
+
.map { lookup.call(Type.constraint(INTERFACE, it, constraint.origin)) }
|
|
73
|
+
.then { Results.sequence(it) }
|
|
74
|
+
.and_then do |deps|
|
|
75
|
+
args = deps.each_index.map { [:impl_arg, it, 'show'] } + [[:var, 'value']]
|
|
76
|
+
|
|
77
|
+
Symbol::DerivedFunction
|
|
78
|
+
.new(params: ['value'], body: [:call, [:stdlib_fn, fn], args])
|
|
79
|
+
.then { Ok[implementation(constraint, { 'show' => it }, deps:)] }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
|
|
60
84
|
def constant(text)
|
|
61
85
|
Symbol::DerivedFunction.new(params: ['value'], body: text)
|
|
62
86
|
end
|
|
@@ -33,9 +33,8 @@ module Jade
|
|
|
33
33
|
[pattern_state, expr_result.constraints]
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
.
|
|
38
|
-
.then { final_state.add_errors(it) }
|
|
36
|
+
final_state
|
|
37
|
+
.defer_patterns([pattern], pattern.range, expr_result.type)
|
|
39
38
|
.unify_result(
|
|
40
39
|
expr_result.with(constraints: residual_cs),
|
|
41
40
|
expected.type,
|
|
@@ -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)
|
|
@@ -54,10 +54,7 @@ module Jade
|
|
|
54
54
|
in AST::Pattern::Binding | AST::Pattern::Wildcard
|
|
55
55
|
acc
|
|
56
56
|
else
|
|
57
|
-
|
|
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
|
|
|
@@ -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)
|
|
@@ -8,6 +8,7 @@ require 'jade/frontend/type_checking/expected'
|
|
|
8
8
|
require 'jade/frontend/type_checking/inference'
|
|
9
9
|
require 'jade/frontend/type_checking/loader'
|
|
10
10
|
require 'jade/frontend/type_checking/narrowing'
|
|
11
|
+
require 'jade/frontend/type_checking/pattern_checks'
|
|
11
12
|
require 'jade/frontend/type_checking/port_resolution'
|
|
12
13
|
require 'jade/frontend/type_checking/requirements'
|
|
13
14
|
require 'jade/frontend/type_checking/result'
|
|
@@ -28,7 +29,7 @@ module Jade
|
|
|
28
29
|
.load(entry, registry)
|
|
29
30
|
.then { collect_constraints(entry, registry, it) }
|
|
30
31
|
.then { check_node(entry.ast, registry, State.init(it), Expected.infer(it.fresh)) }
|
|
31
|
-
.then { |state, _| Requirements.reconcile(entry, registry, state) }
|
|
32
|
+
.then { |state, _| Requirements.reconcile(entry, registry, PatternChecks.run(state)) }
|
|
32
33
|
.then { |amended, state| complete(amended, state, registry) }
|
|
33
34
|
.and_then { PortResolution.resolve(it, registry) }
|
|
34
35
|
end
|
data/lib/jade/stdlib/dict.rb
CHANGED
|
@@ -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))')
|
data/lib/jade/stdlib/set.rb
CHANGED
|
@@ -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/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jade-lang
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.12.
|
|
4
|
+
version: 0.12.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Agustin Cornu
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-09-
|
|
10
|
+
date: 2026-09-21 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|
|
@@ -315,6 +315,7 @@ files:
|
|
|
315
315
|
- lib/jade/frontend/type_checking/instantiation.rb
|
|
316
316
|
- lib/jade/frontend/type_checking/loader.rb
|
|
317
317
|
- lib/jade/frontend/type_checking/narrowing.rb
|
|
318
|
+
- lib/jade/frontend/type_checking/pattern_checks.rb
|
|
318
319
|
- lib/jade/frontend/type_checking/placeholder.rb
|
|
319
320
|
- lib/jade/frontend/type_checking/port_resolution.rb
|
|
320
321
|
- lib/jade/frontend/type_checking/requirements.rb
|