dhall 0.1.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.
@@ -0,0 +1,383 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "promise.rb"
5
+ require "set"
6
+
7
+ require "dhall/ast"
8
+ require "dhall/binary"
9
+ require "dhall/util"
10
+
11
+ module Dhall
12
+ class ImportFailedException < StandardError; end
13
+ class ImportBannedException < ImportFailedException; end
14
+ class ImportLoopException < ImportBannedException; end
15
+
16
+ module Resolvers
17
+ ReadPathSources = lambda do |sources|
18
+ sources.map do |source|
19
+ Promise.resolve(nil).then { source.pathname.binread }
20
+ end
21
+ end
22
+
23
+ PreflightCORS = lambda do |source, parent_origin|
24
+ uri = source.uri
25
+ if parent_origin != "localhost" && parent_origin != source.origin
26
+ req = Net::HTTP::Options.new(uri)
27
+ req["Origin"] = parent_origin
28
+ req["Access-Control-Request-Method"] = "GET"
29
+ req["Access-Control-Request-Headers"] =
30
+ source.headers.map { |h| h.fetch("header").to_s }.join(",")
31
+ r = Net::HTTP.start(
32
+ uri.hostname,
33
+ uri.port,
34
+ use_ssl: uri.scheme == "https"
35
+ ) { |http| http.request(req) }
36
+
37
+ raise ImportFailedException, source if r.code != "200"
38
+ unless r["Access-Control-Allow-Origin"] == parent_origin ||
39
+ r["Access-Control-Allow-Origin"] == "*"
40
+ raise ImportBannedException, source
41
+ end
42
+ end
43
+ end
44
+
45
+ ReadHttpSources = lambda do |sources, parent_origin|
46
+ sources.map do |source|
47
+ Promise.resolve(nil).then do
48
+ PreflightCORS.call(source, parent_origin)
49
+ uri = source.uri
50
+ req = Net::HTTP::Get.new(uri)
51
+ source.headers.each do |header|
52
+ req[header.fetch("header").to_s] = header.fetch("value").to_s
53
+ end
54
+ r = Net::HTTP.start(
55
+ uri.hostname,
56
+ uri.port,
57
+ use_ssl: uri.scheme == "https"
58
+ ) { |http| http.request(req) }
59
+
60
+ raise ImportFailedException, source if r.code != "200"
61
+ r.body
62
+ end
63
+ end
64
+ end
65
+
66
+ RejectSources = lambda do |sources|
67
+ sources.map do |source|
68
+ Promise.new.reject(ImportBannedException.new(source))
69
+ end
70
+ end
71
+
72
+ class ReadPathAndIPFSSources
73
+ def initialize(
74
+ path_reader: ReadPathSources,
75
+ http_reader: ReadHttpSources,
76
+ https_reader: http_reader,
77
+ public_gateway: "cloudflare-ipfs.com"
78
+ )
79
+ @path_reader = path_reader
80
+ @http_reader = http_reader
81
+ @https_reader = https_reader
82
+ @public_gateway = public_gateway
83
+ end
84
+
85
+ def arity
86
+ 1
87
+ end
88
+
89
+ def call(sources)
90
+ @path_reader.call(sources).map.with_index do |promise, idx|
91
+ source = sources[idx]
92
+ if source.is_a?(Import::AbsolutePath) &&
93
+ ["ipfs", "ipns"].include?(source.path.first)
94
+ gateway_fallback(source, promise)
95
+ else
96
+ promise
97
+ end
98
+ end
99
+ end
100
+
101
+ def to_proc
102
+ method(:call).to_proc
103
+ end
104
+
105
+ protected
106
+
107
+ def gateway_fallback(source, promise)
108
+ promise.catch {
109
+ @http_reader.call([
110
+ source.to_uri(Import::Http, "localhost:8000")
111
+ ], "localhost").first
112
+ }.catch do
113
+ @https_reader.call([
114
+ source.to_uri(Import::Https, @public_gateway)
115
+ ], "localhost").first
116
+ end
117
+ end
118
+ end
119
+
120
+ class ResolutionSet
121
+ def initialize(reader)
122
+ @reader = reader
123
+ @parents = []
124
+ @set = Hash.new { |h, k| h[k] = [] }
125
+ end
126
+
127
+ def register(source)
128
+ p = Promise.new
129
+ if @parents.include?(source)
130
+ p.reject(ImportLoopException.new(source))
131
+ else
132
+ @set[source] << p
133
+ end
134
+ p
135
+ end
136
+
137
+ def resolutions
138
+ sources, promises = @set.to_a.transpose
139
+ [Array(sources), Array(promises)]
140
+ end
141
+
142
+ def reader
143
+ lambda do |sources|
144
+ if @reader.arity == 2
145
+ @reader.call(sources, @parents.last&.origin || "localhost")
146
+ else
147
+ @reader.call(sources)
148
+ end
149
+ end
150
+ end
151
+
152
+ def child(parent_source)
153
+ dup.tap do |c|
154
+ c.instance_eval do
155
+ @parents = @parents.dup + [parent_source]
156
+ @set = Hash.new { |h, k| h[k] = [] }
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ class Standard
163
+ def initialize(
164
+ path_reader: ReadPathSources,
165
+ http_reader: ReadHttpSources,
166
+ https_reader: http_reader
167
+ )
168
+ @path_resolutions = ResolutionSet.new(path_reader)
169
+ @http_resolutions = ResolutionSet.new(http_reader)
170
+ @https_resolutions = ResolutionSet.new(https_reader)
171
+ @cache = {}
172
+ end
173
+
174
+ def cache_fetch(key, &fallback)
175
+ @cache.fetch(key) do
176
+ Promise.resolve(nil).then(&fallback).then do |result|
177
+ @cache[key] = result
178
+ end
179
+ end
180
+ end
181
+
182
+ def resolve_path(path_source)
183
+ @path_resolutions.register(path_source)
184
+ end
185
+
186
+ def resolve_http(http_source)
187
+ http_source.headers.resolve(
188
+ resolver: self,
189
+ relative_to: Dhall::Import::RelativePath.new
190
+ ).then do |headers|
191
+ @http_resolutions.register(
192
+ http_source.with(headers: headers.normalize)
193
+ )
194
+ end
195
+ end
196
+
197
+ def resolve_https(https_source)
198
+ https_source.headers.resolve(
199
+ resolver: self,
200
+ relative_to: Dhall::Import::RelativePath.new
201
+ ).then do |headers|
202
+ @https_resolutions.register(
203
+ https_source.with(headers: headers.normalize)
204
+ )
205
+ end
206
+ end
207
+
208
+ def finish!
209
+ [
210
+ @path_resolutions,
211
+ @http_resolutions,
212
+ @https_resolutions
213
+ ].each do |rset|
214
+ Util.match_result_promises(*rset.resolutions, &rset.reader)
215
+ end
216
+ freeze
217
+ end
218
+
219
+ def child(parent_source)
220
+ dup.tap do |c|
221
+ c.instance_eval do
222
+ @path_resolutions = @path_resolutions.child(parent_source)
223
+ @http_resolutions = @http_resolutions.child(parent_source)
224
+ @https_resolutions = @https_resolutions.child(parent_source)
225
+ end
226
+ end
227
+ end
228
+ end
229
+
230
+ class Default < Standard
231
+ def initialize(
232
+ path_reader: ReadPathSources,
233
+ http_reader: ReadHttpSources,
234
+ https_reader: http_reader,
235
+ ipfs_public_gateway: "cloudflare-ipfs.com"
236
+ )
237
+ super(
238
+ path_reader: ReadPathAndIPFSSources.new(
239
+ path_reader: path_reader,
240
+ http_reader: http_reader,
241
+ https_reader: https_reader,
242
+ public_gateway: ipfs_public_gateway
243
+ ),
244
+ http_reader: http_reader,
245
+ https_reader: https_reader
246
+ )
247
+ end
248
+ end
249
+
250
+ class LocalOnly < Standard
251
+ def initialize(path_reader: ReadPathSources)
252
+ super(
253
+ path_reader: path_reader,
254
+ http_reader: RejectSources,
255
+ https_reader: RejectSources
256
+ )
257
+ end
258
+ end
259
+
260
+ class None < Default
261
+ def initialize
262
+ super(
263
+ path_reader: RejectSources,
264
+ http_reader: RejectSources,
265
+ https_reader: RejectSources
266
+ )
267
+ end
268
+ end
269
+ end
270
+
271
+ class ExpressionResolver
272
+ @@registry = {}
273
+
274
+ def self.for(expr)
275
+ @@registry.find { |k, _| k === expr }.last.new(expr)
276
+ end
277
+
278
+ def self.register_for(kase)
279
+ @@registry[kase] = self
280
+ end
281
+
282
+ def initialize(expr)
283
+ @expr = expr
284
+ end
285
+
286
+ def resolve(**kwargs)
287
+ Util.promise_all_hash(
288
+ @expr.to_h.each_with_object({}) { |(attr, value), h|
289
+ h[attr] = ExpressionResolver.for(value).resolve(**kwargs)
290
+ }
291
+ ).then { |h| @expr.with(h) }
292
+ end
293
+
294
+ class ImportResolver < ExpressionResolver
295
+ register_for Import
296
+
297
+ def resolve(resolver:, relative_to:)
298
+ Promise.resolve(nil).then do
299
+ resolver.cache_fetch(@expr.cache_key(relative_to)) do
300
+ resolve_raw(resolver: resolver, relative_to: relative_to)
301
+ end
302
+ end
303
+ end
304
+
305
+ def resolve_raw(resolver:, relative_to:)
306
+ real_path = @expr.real_path(relative_to)
307
+ real_path.resolve(resolver).then do |result|
308
+ @expr.parse_and_check(result).resolve(
309
+ resolver: resolver.child(real_path),
310
+ relative_to: real_path
311
+ )
312
+ end
313
+ end
314
+ end
315
+
316
+ class FallbackResolver < ExpressionResolver
317
+ register_for Operator::ImportFallback
318
+
319
+ def resolve(**kwargs)
320
+ ExpressionResolver.for(@expr.lhs).resolve(**kwargs).catch do
321
+ ExpressionResolver.for(@expr.rhs).resolve(**kwargs)
322
+ end
323
+ end
324
+ end
325
+
326
+ class ArrayResolver < ExpressionResolver
327
+ register_for Util::ArrayOf.new(Expression)
328
+
329
+ def resolve(**kwargs)
330
+ Promise.all(
331
+ @expr.map { |e| ExpressionResolver.for(e).resolve(**kwargs) }
332
+ )
333
+ end
334
+ end
335
+
336
+ class HashResolver < ExpressionResolver
337
+ register_for Util::HashOf.new(
338
+ ValueSemantics::Anything,
339
+ ValueSemantics::Either.new([Expression, nil])
340
+ )
341
+
342
+ def resolve(**kwargs)
343
+ Util.promise_all_hash(Hash[@expr.map do |k, v|
344
+ [k, ExpressionResolver.for(v).resolve(**kwargs)]
345
+ end])
346
+ end
347
+ end
348
+
349
+ class RecordResolver < ExpressionResolver
350
+ register_for Record
351
+
352
+ def resolve(**kwargs)
353
+ ExpressionResolver.for(@expr.record).resolve(**kwargs).then do |h|
354
+ @expr.with(record: h)
355
+ end
356
+ end
357
+ end
358
+
359
+ register_for Expression
360
+
361
+ class IdentityResolver < ExpressionResolver
362
+ register_for Object
363
+
364
+ def resolve(*)
365
+ Promise.resolve(@expr)
366
+ end
367
+ end
368
+ end
369
+
370
+ class Expression
371
+ def resolve(
372
+ resolver: Resolvers::Default.new,
373
+ relative_to: Import::Path.from_string(Pathname.pwd + "file")
374
+ )
375
+ p = ExpressionResolver.for(self).resolve(
376
+ resolver: resolver,
377
+ relative_to: relative_to
378
+ )
379
+ resolver.finish!
380
+ p
381
+ end
382
+ end
383
+ end
@@ -0,0 +1,1209 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dhall/ast"
4
+ require "dhall/normalize"
5
+
6
+ module Dhall
7
+ module TypeChecker
8
+ def self.assert(type, assertion, message)
9
+ raise TypeError, message unless assertion === type
10
+ type
11
+ end
12
+
13
+ def self.assert_type(expr, assertion, message, context:)
14
+ aexpr = self.for(expr).annotate(context)
15
+ type = aexpr.type
16
+ raise TypeError, "#{message}: #{type}" unless assertion === type
17
+ aexpr
18
+ end
19
+
20
+ def self.assert_types_match(a, b, message, context:)
21
+ atype = self.for(a).annotate(context).type
22
+ btype = self.for(b).annotate(context).type
23
+ raise TypeError, "#{message}: #{atype}, #{btype}" unless atype == btype
24
+ atype
25
+ end
26
+
27
+ def self.for(expr)
28
+ @typecheckers.each do |node_matcher, (typechecker, extras)|
29
+ if node_matcher === expr
30
+ msg = [:call, :for, :new].find { |m| typechecker.respond_to?(m) }
31
+ return typechecker.public_send(msg, expr, *extras)
32
+ end
33
+ end
34
+
35
+ raise TypeError, "Unknown expression: #{expr.inspect}"
36
+ end
37
+
38
+ def self.register(typechecker, node_type, *extras)
39
+ @typecheckers ||= {}
40
+ @typecheckers[node_type] ||= [typechecker, extras]
41
+ end
42
+
43
+ def self.type_of(expr)
44
+ return if expr.nil?
45
+ TypeChecker.for(expr).annotate(TypeChecker::Context.new).type
46
+ end
47
+
48
+ class Context
49
+ def initialize(bindings=Hash.new([]))
50
+ @bindings = bindings.freeze
51
+ freeze
52
+ end
53
+
54
+ def fetch(var)
55
+ @bindings[var.name][var.index] ||
56
+ (raise TypeError, "Free variable: #{var}")
57
+ end
58
+
59
+ def add(ftype)
60
+ self.class.new(@bindings.merge(
61
+ ftype.var => [ftype.type] + @bindings[ftype.var]
62
+ )).shift(1, ftype.var, 0)
63
+ end
64
+
65
+ def shift(amount, name, min_index)
66
+ self.class.new(@bindings.merge(
67
+ Hash[@bindings.map do |var, bindings|
68
+ [var, bindings.map { |b| b.shift(amount, name, min_index) }]
69
+ end]
70
+ ))
71
+ end
72
+ end
73
+
74
+ KINDS = [
75
+ Dhall::Variable["Type"],
76
+ Dhall::Variable["Kind"],
77
+ Dhall::Variable["Sort"]
78
+ ].freeze
79
+
80
+ class Variable
81
+ TypeChecker.register self, Dhall::Variable
82
+
83
+ def initialize(var)
84
+ @var = var
85
+ end
86
+
87
+ BUILTIN = {
88
+ "Type" => Dhall::Variable["Kind"],
89
+ "Kind" => Dhall::Variable["Sort"],
90
+ "Bool" => Dhall::Variable["Type"],
91
+ "Natural" => Dhall::Variable["Type"],
92
+ "Integer" => Dhall::Variable["Type"],
93
+ "Double" => Dhall::Variable["Type"],
94
+ "Text" => Dhall::Variable["Type"],
95
+ "List" => Dhall::Forall.of_arguments(
96
+ Dhall::Variable["Type"],
97
+ body: Dhall::Variable["Type"]
98
+ ),
99
+ "Optional" => Dhall::Forall.of_arguments(
100
+ Dhall::Variable["Type"],
101
+ body: Dhall::Variable["Type"]
102
+ ),
103
+ "None" => Dhall::Forall.new(
104
+ var: "A",
105
+ type: Dhall::Variable["Type"],
106
+ body: Dhall::Application.new(
107
+ function: Dhall::Variable["Optional"],
108
+ argument: Dhall::Variable["A"]
109
+ )
110
+ )
111
+ }.freeze
112
+
113
+ def annotate(context)
114
+ raise TypeError, "Sort has no Type, Kind, or Sort" if @var.name == "Sort"
115
+
116
+ Dhall::TypeAnnotation.new(
117
+ value: @var,
118
+ type: BUILTIN.fetch(@var.name) { context.fetch(@var) }
119
+ )
120
+ end
121
+ end
122
+
123
+ class Literal
124
+ TypeChecker.register self, Dhall::Bool
125
+ TypeChecker.register self, Dhall::Natural
126
+ TypeChecker.register self, Dhall::Text
127
+ TypeChecker.register self, Dhall::Integer
128
+ TypeChecker.register self, Dhall::Double
129
+
130
+ def initialize(lit)
131
+ @lit = lit
132
+ @type = Dhall::Variable[lit.class.name.split(/::/).last]
133
+ end
134
+
135
+ def annotate(*)
136
+ Dhall::TypeAnnotation.new(
137
+ value: @lit,
138
+ type: @type
139
+ )
140
+ end
141
+ end
142
+
143
+ class TextLiteral
144
+ TypeChecker.register self, Dhall::TextLiteral
145
+
146
+ def initialize(lit)
147
+ @lit = lit
148
+ end
149
+
150
+ class Chunks
151
+ def initialize(chunks)
152
+ @chunks = chunks
153
+ end
154
+
155
+ def map
156
+ self.class.new(@chunks.map { |c|
157
+ if c.is_a?(Dhall::Text)
158
+ c
159
+ else
160
+ yield c
161
+ end
162
+ })
163
+ end
164
+
165
+ def to_a
166
+ @chunks
167
+ end
168
+ end
169
+
170
+ def annotate(context)
171
+ chunks = Chunks.new(@lit.chunks).map { |c|
172
+ TypeChecker.for(c).annotate(context).tap do |annotated|
173
+ TypeChecker.assert annotated.type, Dhall::Variable["Text"],
174
+ "Cannot interpolate #{annotated.type}"
175
+ end
176
+ }.to_a
177
+
178
+ Dhall::TypeAnnotation.new(
179
+ value: @lit.with(chunks: chunks),
180
+ type: Dhall::Variable["Text"]
181
+ )
182
+ end
183
+ end
184
+
185
+ class If
186
+ TypeChecker.register self, Dhall::If
187
+
188
+ def initialize(expr)
189
+ @expr = expr
190
+ @predicate = TypeChecker.for(expr.predicate)
191
+ @then = TypeChecker.for(expr.then)
192
+ @else = TypeChecker.for(expr.else)
193
+ end
194
+
195
+ class AnnotatedIf
196
+ def initialize(expr, apred, athen, aelse, context:)
197
+ TypeChecker.assert apred.type, Dhall::Variable["Bool"],
198
+ "If must have a predicate of type Bool"
199
+ TypeChecker.assert_type athen.type, Dhall::Variable["Type"],
200
+ "If branches must have types of type Type",
201
+ context: context
202
+ TypeChecker.assert aelse.type, athen.type,
203
+ "If branches have mismatched types"
204
+ @expr = expr.with(predicate: apred, then: athen, else: aelse)
205
+ end
206
+
207
+ def annotation
208
+ Dhall::TypeAnnotation.new(
209
+ value: @expr,
210
+ type: @expr.then.type
211
+ )
212
+ end
213
+ end
214
+
215
+ def annotate(context)
216
+ AnnotatedIf.new(
217
+ @expr,
218
+ @predicate.annotate(context),
219
+ @then.annotate(context),
220
+ @else.annotate(context),
221
+ context: context
222
+ ).annotation
223
+ end
224
+ end
225
+
226
+ class Operator
227
+ {
228
+ Dhall::Operator::And => Dhall::Variable["Bool"],
229
+ Dhall::Operator::Or => Dhall::Variable["Bool"],
230
+ Dhall::Operator::Equal => Dhall::Variable["Bool"],
231
+ Dhall::Operator::NotEqual => Dhall::Variable["Bool"],
232
+ Dhall::Operator::Plus => Dhall::Variable["Natural"],
233
+ Dhall::Operator::Times => Dhall::Variable["Natural"],
234
+ Dhall::Operator::TextConcatenate => Dhall::Variable["Text"]
235
+ }.each do |node_type, type|
236
+ TypeChecker.register self, node_type, type
237
+ end
238
+
239
+ def initialize(expr, type)
240
+ @expr = expr
241
+ @lhs = TypeChecker.for(expr.lhs)
242
+ @rhs = TypeChecker.for(expr.rhs)
243
+ @type = type
244
+ end
245
+
246
+ def annotate(context)
247
+ annotated_lhs = @lhs.annotate(context)
248
+ annotated_rhs = @rhs.annotate(context)
249
+ types = [annotated_lhs.type, annotated_rhs.type]
250
+ if types.any? { |t| t != @type }
251
+ raise TypeError, "Operator arguments not #{@type}: #{types}"
252
+ end
253
+
254
+ Dhall::TypeAnnotation.new(
255
+ value: @expr.with(lhs: annotated_lhs, rhs: annotated_rhs),
256
+ type: @type
257
+ )
258
+ end
259
+ end
260
+
261
+ class OperatorListConcatenate
262
+ TypeChecker.register self, Dhall::Operator::ListConcatenate
263
+
264
+ def initialize(expr)
265
+ @expr = expr
266
+ @lhs = TypeChecker.for(expr.lhs)
267
+ @rhs = TypeChecker.for(expr.rhs)
268
+ end
269
+
270
+ module IsList
271
+ def self.===(other)
272
+ other.is_a?(Dhall::Application) &&
273
+ other.function == Dhall::Variable["List"]
274
+ end
275
+ end
276
+
277
+ def annotate(context)
278
+ annotated_lhs = @lhs.annotate(context)
279
+ annotated_rhs = @rhs.annotate(context)
280
+
281
+ types = [annotated_lhs.type, annotated_rhs.type]
282
+ assertion = Util::ArrayOf.new(Util::AllOf.new(IsList, types.first))
283
+ TypeChecker.assert types, assertion,
284
+ "Operator arguments wrong: #{types}"
285
+
286
+ Dhall::TypeAnnotation.new(
287
+ value: @expr.with(lhs: annotated_lhs, rhs: annotated_rhs),
288
+ type: types.first
289
+ )
290
+ end
291
+ end
292
+
293
+ class OperatorRecursiveRecordMerge
294
+ TypeChecker.register self, Dhall::Operator::RecursiveRecordMerge
295
+
296
+ def initialize(expr)
297
+ @expr = expr
298
+ @lhs = TypeChecker.for(expr.lhs)
299
+ @rhs = TypeChecker.for(expr.rhs)
300
+ end
301
+
302
+ def annotate(context)
303
+ annotated_lhs = @lhs.annotate(context)
304
+ annotated_rhs = @rhs.annotate(context)
305
+
306
+ type = annotated_lhs.type.deep_merge_type(annotated_rhs.type)
307
+
308
+ TypeChecker.assert type, Dhall::RecordType,
309
+ "RecursiveRecordMerge got #{type}"
310
+
311
+ # Annotate to sanity check
312
+ TypeChecker.for(type).annotate(context)
313
+
314
+ Dhall::TypeAnnotation.new(
315
+ value: @expr.with(lhs: annotated_lhs, rhs: annotated_rhs),
316
+ type: type
317
+ )
318
+ end
319
+ end
320
+
321
+ class OperatorRightBiasedRecordMerge
322
+ TypeChecker.register self, Dhall::Operator::RightBiasedRecordMerge
323
+
324
+ def initialize(expr)
325
+ @expr = expr
326
+ end
327
+
328
+ def check(context)
329
+ annotated_lhs = TypeChecker.assert_type @expr.lhs, Dhall::RecordType,
330
+ "RecursiveRecordMerge got",
331
+ context: context
332
+
333
+ annotated_rhs = TypeChecker.assert_type @expr.rhs, Dhall::RecordType,
334
+ "RecursiveRecordMerge got",
335
+ context: context
336
+
337
+ TypeChecker.assert_types_match annotated_lhs.type, annotated_rhs.type,
338
+ "RecursiveRecordMerge got mixed kinds",
339
+ context: context
340
+
341
+ [annotated_lhs, annotated_rhs]
342
+ end
343
+
344
+ def annotate(context)
345
+ annotated_lhs, annotated_rhs = check(context)
346
+
347
+ Dhall::TypeAnnotation.new(
348
+ value: @expr.with(lhs: annotated_lhs, rhs: annotated_rhs),
349
+ type: TypeChecker.for(
350
+ annotated_lhs.type.merge_type(annotated_rhs.type)
351
+ ).annotate(context).value
352
+ )
353
+ end
354
+ end
355
+
356
+ class OperatorRecursiveRecordTypeMerge
357
+ TypeChecker.register self, Dhall::Operator::RecursiveRecordTypeMerge
358
+
359
+ def initialize(expr)
360
+ @expr = expr
361
+ end
362
+
363
+ def annotate(context)
364
+ kind = TypeChecker.assert_types_match(
365
+ @expr.lhs, @expr.rhs,
366
+ "RecursiveRecordTypeMerge mixed kinds",
367
+ context: context
368
+ )
369
+
370
+ type = @expr.lhs.deep_merge_type(@expr.rhs)
371
+
372
+ TypeChecker.assert type, Dhall::RecordType,
373
+ "RecursiveRecordMerge got #{type}"
374
+
375
+ # Annotate to sanity check
376
+ TypeChecker.for(type).annotate(context)
377
+
378
+ Dhall::TypeAnnotation.new(value: @expr, type: kind)
379
+ end
380
+ end
381
+
382
+ class EmptyList
383
+ TypeChecker.register self, Dhall::EmptyList
384
+
385
+ def initialize(expr)
386
+ @expr = expr
387
+ end
388
+
389
+ def annotate(context)
390
+ TypeChecker.assert_type @expr.element_type, Dhall::Variable["Type"],
391
+ "EmptyList element type not of type Type",
392
+ context: context
393
+
394
+ Dhall::TypeAnnotation.new(type: @expr.type, value: @expr)
395
+ end
396
+ end
397
+
398
+ class List
399
+ TypeChecker.register self, Dhall::List
400
+
401
+ def initialize(list)
402
+ @list = list
403
+ end
404
+
405
+ class AnnotatedList
406
+ def initialize(alist)
407
+ @alist = alist
408
+ end
409
+
410
+ def annotation
411
+ list = @alist.with(element_type: element_type)
412
+ Dhall::TypeAnnotation.new(type: list.type, value: list)
413
+ end
414
+
415
+ def element_type
416
+ @alist.first.value&.type || @alist.element_type
417
+ end
418
+
419
+ def element_types
420
+ @alist.to_a.map(&:type)
421
+ end
422
+ end
423
+
424
+ def annotate(context)
425
+ alist = AnnotatedList.new(@list.map(type: @list.element_type) { |el|
426
+ TypeChecker.for(el).annotate(context)
427
+ })
428
+
429
+ TypeChecker.assert alist.element_types,
430
+ Util::ArrayOf.new(alist.element_type),
431
+ "Non-homogenous List"
432
+
433
+ TypeChecker.assert_type alist.element_type, Dhall::Variable["Type"],
434
+ "List type not of type Type", context: context
435
+
436
+ alist.annotation
437
+ end
438
+ end
439
+
440
+ class OptionalNone
441
+ TypeChecker.register self, Dhall::OptionalNone
442
+
443
+ def initialize(expr)
444
+ @expr = expr
445
+ end
446
+
447
+ def annotate(context)
448
+ TypeChecker.assert(
449
+ TypeChecker.for(@expr.value_type).annotate(context).type,
450
+ Dhall::Variable["Type"],
451
+ "OptionalNone element type not of type Type"
452
+ )
453
+
454
+ Dhall::TypeAnnotation.new(type: @expr.type, value: @expr)
455
+ end
456
+ end
457
+
458
+ class Optional
459
+ TypeChecker.register self, Dhall::Optional
460
+
461
+ def initialize(some)
462
+ @some = some
463
+ end
464
+
465
+ def annotate(context)
466
+ asome = @some.map do |el|
467
+ TypeChecker.for(el).annotate(context)
468
+ end
469
+ some = asome.with(value_type: asome.value.type)
470
+
471
+ type_type = TypeChecker.for(some.value_type).annotate(context).type
472
+ TypeChecker.assert type_type, Dhall::Variable["Type"],
473
+ "Some type not of type Type, was: #{type_type}"
474
+
475
+ Dhall::TypeAnnotation.new(type: some.type, value: some)
476
+ end
477
+ end
478
+
479
+ class EmptyAnonymousType
480
+ TypeChecker.register self, Dhall::EmptyRecordType
481
+
482
+ def initialize(expr)
483
+ @expr = expr
484
+ end
485
+
486
+ def annotate(*)
487
+ Dhall::TypeAnnotation.new(
488
+ value: @expr,
489
+ type: Dhall::Variable["Type"]
490
+ )
491
+ end
492
+ end
493
+
494
+ class AnonymousType
495
+ TypeChecker.register self, Dhall::RecordType
496
+ TypeChecker.register self, Dhall::UnionType
497
+
498
+ def initialize(type)
499
+ @type = type
500
+ end
501
+
502
+ def annotate(context)
503
+ kinds = @type.record.values.compact.map do |mtype|
504
+ TypeChecker.for(mtype).annotate(context).type
505
+ end
506
+
507
+ TypeChecker.assert (kinds - KINDS), [],
508
+ "AnonymousType field kind not one of #{KINDS}"
509
+
510
+ TypeChecker.assert kinds, Util::ArrayAllTheSame,
511
+ "AnonymousType field kinds not all the same"
512
+
513
+ type = kinds.first || KINDS.first
514
+ Dhall::TypeAnnotation.new(value: @type, type: type)
515
+ end
516
+ end
517
+
518
+ class EmptyRecord
519
+ TypeChecker.register self, Dhall::EmptyRecord
520
+
521
+ def initialize(expr)
522
+ @expr = expr
523
+ end
524
+
525
+ def annotate(*)
526
+ Dhall::TypeAnnotation.new(
527
+ value: @expr,
528
+ type: Dhall::EmptyRecordType.new
529
+ )
530
+ end
531
+ end
532
+
533
+ class Record
534
+ TypeChecker.register self, Dhall::Record
535
+
536
+ def initialize(record)
537
+ @record = record
538
+ end
539
+
540
+ def annotate(context)
541
+ arecord = @record.map do |k, v|
542
+ [k, TypeChecker.for(v).annotate(context)]
543
+ end
544
+
545
+ Dhall::TypeAnnotation.new(
546
+ value: arecord,
547
+ type: TypeChecker.for(Dhall::RecordType.for(Hash[
548
+ arecord.record.map { |k, v| [k, v.type] }
549
+ ])).annotate(context).value
550
+ )
551
+ end
552
+ end
553
+
554
+ class RecordSelection
555
+ TypeChecker.register self, Dhall::RecordSelection
556
+
557
+ def initialize(selection)
558
+ @selection = selection
559
+ @record = selection.record
560
+ @selector = selection.selector
561
+ end
562
+
563
+ class Selector
564
+ def self.for(annotated_record)
565
+ if annotated_record.type == Dhall::Variable["Type"]
566
+ TypeSelector.new(annotated_record.value)
567
+ elsif annotated_record.type.class == Dhall::RecordType
568
+ new(annotated_record.type)
569
+ else
570
+ raise TypeError, "RecordSelection on #{annotated_record.type}"
571
+ end
572
+ end
573
+
574
+ def initialize(type)
575
+ @fetch_from = type.record
576
+ end
577
+
578
+ def select(selector)
579
+ @fetch_from.fetch(selector) do
580
+ raise TypeError, "#{@fetch_from} has no field #{@selector}"
581
+ end
582
+ end
583
+ end
584
+
585
+ class TypeSelector < Selector
586
+ def initialize(union)
587
+ normalized = union.normalize
588
+ TypeChecker.assert normalized, Dhall::UnionType,
589
+ "RecordSelection on #{normalized}"
590
+ @fetch_from = normalized.constructor_types
591
+ end
592
+ end
593
+
594
+ def annotate(context)
595
+ arecord = TypeChecker.for(@record).annotate(context)
596
+ selector = Selector.for(arecord)
597
+
598
+ Dhall::TypeAnnotation.new(
599
+ value: @selection.with(record: arecord),
600
+ type: selector.select(@selector)
601
+ )
602
+ end
603
+ end
604
+
605
+ class RecordProjection
606
+ TypeChecker.register self, Dhall::EmptyRecordProjection
607
+ TypeChecker.register self, Dhall::RecordProjection
608
+
609
+ def initialize(projection)
610
+ @projection = projection
611
+ @record = TypeChecker.for(projection.record)
612
+ @selectors = projection.selectors
613
+ end
614
+
615
+ def annotate(context)
616
+ arecord = @record.annotate(context)
617
+
618
+ TypeChecker.assert arecord.type.class.name, "Dhall::RecordType",
619
+ "RecordProjection on #{arecord.type}"
620
+
621
+ slice = arecord.type.slice(@selectors)
622
+ TypeChecker.assert slice.keys, @selectors,
623
+ "#{arecord.type} missing one of: #{@selectors}"
624
+
625
+ Dhall::TypeAnnotation.new(
626
+ value: @projection.with(record: arecord),
627
+ type: slice
628
+ )
629
+ end
630
+ end
631
+
632
+ class Union
633
+ TypeChecker.register self, Dhall::Union
634
+
635
+ def initialize(union)
636
+ @union = union
637
+ @value = TypeChecker.for(union.value)
638
+ end
639
+
640
+ def annotate(context)
641
+ annotated_value = @value.annotate(context)
642
+
643
+ type = Dhall::UnionType.new(
644
+ alternatives: { @union.tag => annotated_value.type }
645
+ ).merge(@union.alternatives)
646
+
647
+ # Annotate to sanity check
648
+ TypeChecker.for(type).annotate(context)
649
+
650
+ Dhall::TypeAnnotation.new(
651
+ value: @union.with(value: annotated_value),
652
+ type: type
653
+ )
654
+ end
655
+ end
656
+
657
+ class Merge
658
+ TypeChecker.register self, Dhall::Merge
659
+
660
+ def initialize(merge)
661
+ @merge = merge
662
+ @record = TypeChecker.for(merge.record)
663
+ @union = TypeChecker.for(merge.input)
664
+ end
665
+
666
+ class Handlers
667
+ def initialize(annotation)
668
+ @type = annotation.type
669
+
670
+ TypeChecker.assert @type, Dhall::RecordType,
671
+ "Merge expected Record got: #{@type}"
672
+ end
673
+
674
+ def output_type(output_annotation=nil)
675
+ @type.record.values.reduce(output_annotation) do |type_acc, htype|
676
+ htype = htype.body.shift(-1, htype.var, 0) if htype.is_a?(Dhall::Forall)
677
+
678
+ if type_acc && htype.normalize != type_acc.normalize
679
+ raise TypeError, "Handler output types must all match"
680
+ end
681
+
682
+ htype
683
+ end
684
+ end
685
+
686
+ def keys
687
+ @type.record.keys
688
+ end
689
+
690
+ def fetch_input_type(k)
691
+ type = @type.record.fetch(k) do
692
+ raise TypeError, "No merge handler for alternative: #{k}"
693
+ end
694
+
695
+ TypeChecker.assert type, Dhall::Forall, "Handler is not a function"
696
+
697
+ type.type
698
+ end
699
+ end
700
+
701
+ class AnnotatedMerge
702
+ def initialize(merge:, record:, input:)
703
+ @merge = merge.with(record: record, input: input)
704
+ @handlers = Handlers.new(record)
705
+ @record = record
706
+ @union = input
707
+
708
+ TypeChecker.assert @union.type, Dhall::UnionType,
709
+ "Merge expected Union got: #{@union.type}"
710
+
711
+ assert_union_and_handlers_match
712
+ end
713
+
714
+ def annotation
715
+ Dhall::TypeAnnotation.new(
716
+ value: @merge,
717
+ type: type
718
+ )
719
+ end
720
+
721
+ def type
722
+ @type ||= @handlers.output_type(@merge.type)
723
+ end
724
+
725
+ def assert_kind(context)
726
+ kind = TypeChecker.for(type).annotate(context).type
727
+
728
+ TypeChecker.assert(
729
+ kind,
730
+ Dhall::Variable["Type"],
731
+ "Merge must have kind Type"
732
+ )
733
+
734
+ kind
735
+ end
736
+
737
+ def assert_union_and_handlers_match
738
+ extras = @handlers.keys - @union.type.alternatives.keys
739
+ TypeChecker.assert extras, [],
740
+ "Merge handlers unknown alternatives: #{extras}"
741
+
742
+ @union.type.alternatives.each do |k, atype|
743
+ atype.nil? || TypeChecker.assert(
744
+ @handlers.fetch_input_type(k),
745
+ atype,
746
+ "Handler argument does not match alternative type: #{atype}"
747
+ )
748
+ end
749
+ end
750
+ end
751
+
752
+ def annotate(context)
753
+ amerge = AnnotatedMerge.new(
754
+ merge: @merge,
755
+ record: @record.annotate(context),
756
+ input: @union.annotate(context)
757
+ )
758
+ amerge.assert_kind(context)
759
+ amerge.annotation
760
+ end
761
+ end
762
+
763
+ class Forall
764
+ TypeChecker.register self, Dhall::Forall
765
+
766
+ def initialize(expr)
767
+ @expr = expr
768
+ @var = expr.var
769
+ @var_type = expr.type
770
+ @input = TypeChecker.for(expr.type)
771
+ @output = TypeChecker.for(expr.body)
772
+ end
773
+
774
+ module FunctionKind
775
+ def self.for(inkind, outkind)
776
+ if inkind.nil? || outkind.nil?
777
+ raise TypeError, "FunctionType part of this is a term"
778
+ end
779
+
780
+ raise TypeError, "Dependent types are not allowed" if outkind > inkind
781
+
782
+ if outkind.zero?
783
+ Term.new
784
+ else
785
+ Polymorphic.new(inkind, outkind)
786
+ end
787
+ end
788
+
789
+ class Term
790
+ def kind
791
+ KINDS.first
792
+ end
793
+ end
794
+
795
+ class Polymorphic
796
+ def initialize(inkind, outkind)
797
+ @inkind = inkind
798
+ @outkind = outkind
799
+ end
800
+
801
+ def kind
802
+ KINDS[[@outkind, @inkind].max]
803
+ end
804
+ end
805
+ end
806
+
807
+ def annotate(context)
808
+ inkind = KINDS.index(@input.annotate(context).type)
809
+ outkind = KINDS.index(@output.annotate(context.add(@expr)).type)
810
+
811
+ Dhall::TypeAnnotation.new(
812
+ value: @expr,
813
+ type: FunctionKind.for(inkind, outkind).kind
814
+ )
815
+ end
816
+ end
817
+
818
+ class Function
819
+ TypeChecker.register self, Dhall::Function
820
+
821
+ def initialize(func)
822
+ @func = func
823
+ @type = Dhall::Forall.new(
824
+ var: func.var,
825
+ type: func.type,
826
+ body: Dhall::Variable["UNKNOWN"]
827
+ )
828
+ @output = TypeChecker.for(func.body)
829
+ end
830
+
831
+ def annotate(context)
832
+ abody = @output.annotate(context.add(@type))
833
+
834
+ Dhall::TypeAnnotation.new(
835
+ value: @func.with(body: abody),
836
+ type: TypeChecker.for(
837
+ @type.with(body: abody.type)
838
+ ).annotate(context).value
839
+ )
840
+ end
841
+ end
842
+
843
+ class Application
844
+ TypeChecker.register self, Dhall::Application
845
+
846
+ def initialize(app)
847
+ @app = app
848
+ @func = TypeChecker.for(app.function)
849
+ @arg = app.argument
850
+ end
851
+
852
+ def annotate(context)
853
+ afunc = @func.annotate(context)
854
+
855
+ TypeChecker.assert afunc.type, Dhall::Forall,
856
+ "Application LHS is not a function"
857
+
858
+ aarg = TypeChecker.for(
859
+ Dhall::TypeAnnotation.new(value: @arg, type: afunc.type.type)
860
+ ).annotate(context)
861
+
862
+ Dhall::TypeAnnotation.new(
863
+ value: @app.with(function: afunc, argument: aarg),
864
+ type: afunc.type.call(aarg.value)
865
+ )
866
+ end
867
+ end
868
+
869
+ TypeChecker.register ->(blk) { LetIn.for(blk.unflatten) }, Dhall::LetBlock
870
+
871
+ class LetIn
872
+ TypeChecker.register self, Dhall::LetIn
873
+
874
+ def self.for(letin)
875
+ if letin.let.type
876
+ LetInAnnotated.new(letin)
877
+ else
878
+ LetIn.new(letin)
879
+ end
880
+ end
881
+
882
+ def initialize(letin)
883
+ @letin = letin
884
+ @let = @letin.let
885
+ end
886
+
887
+ def annotate(context)
888
+ alet = @let.with(type: assign_type(context))
889
+ type = TypeChecker.for(@letin.eliminate).annotate(context).type
890
+ abody = Dhall::TypeAnnotation.new(value: @letin.body, type: type)
891
+ Dhall::TypeAnnotation.new(
892
+ value: @letin.with(let: alet, body: abody),
893
+ type: type
894
+ )
895
+ end
896
+
897
+ protected
898
+
899
+ def assign_type(context)
900
+ TypeChecker.for(@let.assign).annotate(context).type
901
+ end
902
+ end
903
+
904
+ class LetInAnnotated < LetIn
905
+ protected
906
+
907
+ def assign_type(context)
908
+ TypeChecker.for(
909
+ Dhall::TypeAnnotation.new(
910
+ value: @let.assign,
911
+ type: @let.type
912
+ )
913
+ ).annotate(context).type
914
+ end
915
+ end
916
+
917
+ class TypeAnnotation
918
+ TypeChecker.register self, Dhall::TypeAnnotation
919
+
920
+ def initialize(expr)
921
+ @expr = expr
922
+ end
923
+
924
+ def annotate(context)
925
+ redo_annotation = TypeChecker.for(@expr.value).annotate(context)
926
+
927
+ if redo_annotation.type.normalize == @expr.type.normalize
928
+ redo_annotation
929
+ else
930
+ raise TypeError, "TypeAnnotation does not match: " \
931
+ "#{@expr.type}, #{redo_annotation.type}"
932
+ end
933
+ end
934
+ end
935
+
936
+ BUILTIN_TYPES = {
937
+ "Natural/build" => Dhall::Forall.of_arguments(
938
+ Dhall::Forall.new(
939
+ var: "natural",
940
+ type: Dhall::Variable["Type"],
941
+ body: Dhall::Forall.new(
942
+ var: "succ",
943
+ type: Dhall::Forall.of_arguments(
944
+ Dhall::Variable["natural"],
945
+ body: Dhall::Variable["natural"]
946
+ ),
947
+ body: Dhall::Forall.new(
948
+ var: "zero",
949
+ type: Dhall::Variable["natural"],
950
+ body: Dhall::Variable["natural"]
951
+ )
952
+ )
953
+ ),
954
+ body: Dhall::Variable["Natural"]
955
+ ),
956
+ "Natural/fold" => Dhall::Forall.of_arguments(
957
+ Dhall::Variable["Natural"],
958
+ body: Dhall::Forall.new(
959
+ var: "natural",
960
+ type: Dhall::Variable["Type"],
961
+ body: Dhall::Forall.new(
962
+ var: "succ",
963
+ type: Dhall::Forall.of_arguments(
964
+ Dhall::Variable["natural"],
965
+ body: Dhall::Variable["natural"]
966
+ ),
967
+ body: Dhall::Forall.new(
968
+ var: "zero",
969
+ type: Dhall::Variable["natural"],
970
+ body: Dhall::Variable["natural"]
971
+ )
972
+ )
973
+ )
974
+ ),
975
+ "Natural/isZero" => Dhall::Forall.of_arguments(
976
+ Dhall::Variable["Natural"],
977
+ body: Dhall::Variable["Bool"]
978
+ ),
979
+ "Natural/even" => Dhall::Forall.of_arguments(
980
+ Dhall::Variable["Natural"],
981
+ body: Dhall::Variable["Bool"]
982
+ ),
983
+ "Natural/odd" => Dhall::Forall.of_arguments(
984
+ Dhall::Variable["Natural"],
985
+ body: Dhall::Variable["Bool"]
986
+ ),
987
+ "Natural/toInteger" => Dhall::Forall.of_arguments(
988
+ Dhall::Variable["Natural"],
989
+ body: Dhall::Variable["Integer"]
990
+ ),
991
+ "Natural/show" => Dhall::Forall.of_arguments(
992
+ Dhall::Variable["Natural"],
993
+ body: Dhall::Variable["Text"]
994
+ ),
995
+ "Text/show" => Dhall::Forall.of_arguments(
996
+ Dhall::Variable["Text"],
997
+ body: Dhall::Variable["Text"]
998
+ ),
999
+ "List/build" => Dhall::Forall.new(
1000
+ var: "a",
1001
+ type: Dhall::Variable["Type"],
1002
+ body: Dhall::Forall.of_arguments(
1003
+ Dhall::Forall.new(
1004
+ var: "list",
1005
+ type: Dhall::Variable["Type"],
1006
+ body: Dhall::Forall.new(
1007
+ var: "cons",
1008
+ type: Dhall::Forall.of_arguments(
1009
+ Dhall::Variable["a"],
1010
+ Dhall::Variable["list"],
1011
+ body: Dhall::Variable["list"]
1012
+ ),
1013
+ body: Dhall::Forall.new(
1014
+ var: "nil",
1015
+ type: Dhall::Variable["list"],
1016
+ body: Dhall::Variable["list"]
1017
+ )
1018
+ )
1019
+ ),
1020
+ body: Dhall::Application.new(
1021
+ function: Dhall::Variable["List"],
1022
+ argument: Dhall::Variable["a"]
1023
+ )
1024
+ )
1025
+ ),
1026
+ "List/fold" => Dhall::Forall.new(
1027
+ var: "a",
1028
+ type: Dhall::Variable["Type"],
1029
+ body: Dhall::Forall.of_arguments(
1030
+ Dhall::Application.new(
1031
+ function: Dhall::Variable["List"],
1032
+ argument: Dhall::Variable["a"]
1033
+ ),
1034
+ body: Dhall::Forall.new(
1035
+ var: "list",
1036
+ type: Dhall::Variable["Type"],
1037
+ body: Dhall::Forall.new(
1038
+ var: "cons",
1039
+ type: Dhall::Forall.of_arguments(
1040
+ Dhall::Variable["a"],
1041
+ Dhall::Variable["list"],
1042
+ body: Dhall::Variable["list"]
1043
+ ),
1044
+ body: Dhall::Forall.new(
1045
+ var: "nil",
1046
+ type: Dhall::Variable["list"],
1047
+ body: Dhall::Variable["list"]
1048
+ )
1049
+ )
1050
+ )
1051
+ )
1052
+ ),
1053
+ "List/length" => Dhall::Forall.new(
1054
+ var: "a",
1055
+ type: Dhall::Variable["Type"],
1056
+ body: Dhall::Forall.of_arguments(
1057
+ Dhall::Application.new(
1058
+ function: Dhall::Variable["List"],
1059
+ argument: Dhall::Variable["a"]
1060
+ ),
1061
+ body: Dhall::Variable["Natural"]
1062
+ )
1063
+ ),
1064
+ "List/head" => Dhall::Forall.new(
1065
+ var: "a",
1066
+ type: Dhall::Variable["Type"],
1067
+ body: Dhall::Forall.of_arguments(
1068
+ Dhall::Application.new(
1069
+ function: Dhall::Variable["List"],
1070
+ argument: Dhall::Variable["a"]
1071
+ ),
1072
+ body: Dhall::Application.new(
1073
+ function: Dhall::Variable["Optional"],
1074
+ argument: Dhall::Variable["a"]
1075
+ )
1076
+ )
1077
+ ),
1078
+ "List/last" => Dhall::Forall.new(
1079
+ var: "a",
1080
+ type: Dhall::Variable["Type"],
1081
+ body: Dhall::Forall.of_arguments(
1082
+ Dhall::Application.new(
1083
+ function: Dhall::Variable["List"],
1084
+ argument: Dhall::Variable["a"]
1085
+ ),
1086
+ body: Dhall::Application.new(
1087
+ function: Dhall::Variable["Optional"],
1088
+ argument: Dhall::Variable["a"]
1089
+ )
1090
+ )
1091
+ ),
1092
+ "List/indexed" => Dhall::Forall.new(
1093
+ var: "a",
1094
+ type: Dhall::Variable["Type"],
1095
+ body: Dhall::Forall.of_arguments(
1096
+ Dhall::Application.new(
1097
+ function: Dhall::Variable["List"],
1098
+ argument: Dhall::Variable["a"]
1099
+ ),
1100
+ body: Dhall::Application.new(
1101
+ function: Dhall::Variable["List"],
1102
+ argument: Dhall::RecordType.new(
1103
+ record: {
1104
+ "index" => Dhall::Variable["Natural"],
1105
+ "value" => Dhall::Variable["a"]
1106
+ }
1107
+ )
1108
+ )
1109
+ )
1110
+ ),
1111
+ "List/reverse" => Dhall::Forall.new(
1112
+ var: "a",
1113
+ type: Dhall::Variable["Type"],
1114
+ body: Dhall::Forall.of_arguments(
1115
+ Dhall::Application.new(
1116
+ function: Dhall::Variable["List"],
1117
+ argument: Dhall::Variable["a"]
1118
+ ),
1119
+ body: Dhall::Application.new(
1120
+ function: Dhall::Variable["List"],
1121
+ argument: Dhall::Variable["a"]
1122
+ )
1123
+ )
1124
+ ),
1125
+ "Optional/fold" => Dhall::Forall.new(
1126
+ var: "a",
1127
+ type: Dhall::Variable["Type"],
1128
+ body: Dhall::Forall.of_arguments(
1129
+ Dhall::Application.new(
1130
+ function: Dhall::Variable["Optional"],
1131
+ argument: Dhall::Variable["a"]
1132
+ ),
1133
+ body: Dhall::Forall.new(
1134
+ var: "optional",
1135
+ type: Dhall::Variable["Type"],
1136
+ body: Dhall::Forall.new(
1137
+ var: "just",
1138
+ type: Dhall::Forall.of_arguments(
1139
+ Dhall::Variable["a"],
1140
+ body: Dhall::Variable["optional"]
1141
+ ),
1142
+ body: Dhall::Forall.new(
1143
+ var: "nothing",
1144
+ type: Dhall::Variable["optional"],
1145
+ body: Dhall::Variable["optional"]
1146
+ )
1147
+ )
1148
+ )
1149
+ )
1150
+ ),
1151
+ "Optional/build" => Dhall::Forall.new(
1152
+ var: "a",
1153
+ type: Dhall::Variable["Type"],
1154
+ body: Dhall::Forall.of_arguments(
1155
+ Dhall::Forall.new(
1156
+ var: "optional",
1157
+ type: Dhall::Variable["Type"],
1158
+ body: Dhall::Forall.new(
1159
+ var: "just",
1160
+ type: Dhall::Forall.of_arguments(
1161
+ Dhall::Variable["a"],
1162
+ body: Dhall::Variable["optional"]
1163
+ ),
1164
+ body: Dhall::Forall.new(
1165
+ var: "nothing",
1166
+ type: Dhall::Variable["optional"],
1167
+ body: Dhall::Variable["optional"]
1168
+ )
1169
+ )
1170
+ ),
1171
+ body: Dhall::Application.new(
1172
+ function: Dhall::Variable["Optional"],
1173
+ argument: Dhall::Variable["a"]
1174
+ )
1175
+ )
1176
+ ),
1177
+ "Integer/show" => Dhall::Forall.of_arguments(
1178
+ Dhall::Variable["Integer"],
1179
+ body: Dhall::Variable["Text"]
1180
+ ),
1181
+ "Integer/toDouble" => Dhall::Forall.of_arguments(
1182
+ Dhall::Variable["Integer"],
1183
+ body: Dhall::Variable["Double"]
1184
+ ),
1185
+ "Double/show" => Dhall::Forall.of_arguments(
1186
+ Dhall::Variable["Double"],
1187
+ body: Dhall::Variable["Text"]
1188
+ )
1189
+ }.freeze
1190
+
1191
+ class Builtin
1192
+ TypeChecker.register self, Dhall::Builtin
1193
+
1194
+ def initialize(builtin)
1195
+ @expr = builtin
1196
+ @name = builtin.as_json
1197
+ end
1198
+
1199
+ def annotate(*)
1200
+ Dhall::TypeAnnotation.new(
1201
+ value: @expr,
1202
+ type: BUILTIN_TYPES.fetch(@name) do
1203
+ raise TypeError, "Unknown Builtin #{@name}"
1204
+ end
1205
+ )
1206
+ end
1207
+ end
1208
+ end
1209
+ end