# frozen_string_literal: true
require "dhall/ast"
require "dhall/normalize"
module Dhall
module TypeChecker
def self.assert(type, assertion, message)
unless assertion === type
raise TypeError, message
end
end
def self.assert_type(expr, assertion, message, context:)
unless assertion === self.for(expr).annotate(context).type
raise TypeError, message
end
end
def self.for(expr)
case expr
when Dhall::Variable
Variable.new(expr)
when Dhall::Bool, Dhall::Natural, Dhall::Text, Dhall::Integer,
Dhall::Double
Literal.new(expr)
when Dhall::TextLiteral
TextLiteral.new(expr)
when Dhall::EmptyList
EmptyList.new(expr)
when Dhall::List
List.new(expr)
when Dhall::OptionalNone
OptionalNone.new(expr)
when Dhall::Optional
Optional.new(expr)
when Dhall::If
If.new(expr)
when Dhall::Application
Application.new(expr)
when Dhall::Operator::And, Dhall::Operator::Or,
Dhall::Operator::Equal, Dhall::Operator::NotEqual
Operator.new(expr, Dhall::Variable["Bool"])
when Dhall::Operator::Plus, Dhall::Operator::Times
Operator.new(expr, Dhall::Variable["Natural"])
when Dhall::Operator::TextConcatenate
Operator.new(expr, Dhall::Variable["Text"])
when Dhall::Operator::ListConcatenate
OperatorListConcatenate.new(expr)
when Dhall::Operator::RecursiveRecordMerge
OperatorRecursiveRecordMerge.new(expr)
when Dhall::Operator::RightBiasedRecordMerge
OperatorRightBiasedRecordMerge.new(expr)
when Dhall::Operator::RecursiveRecordTypeMerge
OperatorRecursiveRecordTypeMerge.new(expr)
when Dhall::EmptyRecordType
EmptyAnonymousType.new(expr)
when Dhall::RecordType, Dhall::UnionType
AnonymousType.new(expr)
when Dhall::EmptyRecord
EmptyRecord.new(expr)
when Dhall::Record
Record.new(expr)
when Dhall::RecordSelection
RecordSelection.new(expr)
when Dhall::RecordProjection, Dhall::EmptyRecordProjection
RecordProjection.new(expr)
when Dhall::Union
Union.new(expr)
when Dhall::Merge
Merge.new(expr)
when Dhall::Forall
Forall.new(expr)
when Dhall::Function
Function.new(expr)
when Dhall::LetBlock
LetBlock.for(expr)
when Dhall::TypeAnnotation
TypeAnnotation.new(expr)
when Dhall::Builtin
Builtin.new(expr)
else
raise TypeError, "Unknown expression: #{expr}"
end
end
class Context
def initialize(bindings=Hash.new([]))
@bindings = bindings.freeze
freeze
end
def fetch(var)
@bindings[var.name][var.index] ||
(raise TypeError, "Free variable: #{var}")
end
def add(ftype)
self.class.new(@bindings.merge(
ftype.var => [ftype.type] + @bindings[ftype.var]
)).shift(1, ftype.var, 0)
end
def shift(amount, name, min_index)
self.class.new(@bindings.merge(
Hash[@bindings.map do |var, bindings|
[var, bindings.map { |b| b.shift(amount, name, min_index) }]
end]
))
end
end
KINDS = [
Dhall::Variable["Type"],
Dhall::Variable["Kind"],
Dhall::Variable["Sort"]
].freeze
class Variable
def initialize(var)
@var = var
end
BUILTIN = {
"Type" => Dhall::Variable["Kind"],
"Kind" => Dhall::Variable["Sort"],
"Bool" => Dhall::Variable["Type"],
"Natural" => Dhall::Variable["Type"],
"Integer" => Dhall::Variable["Type"],
"Double" => Dhall::Variable["Type"],
"Text" => Dhall::Variable["Type"],
"List" => Dhall::Forall.of_arguments(
Dhall::Variable["Type"],
body: Dhall::Variable["Type"]
),
"Optional" => Dhall::Forall.of_arguments(
Dhall::Variable["Type"],
body: Dhall::Variable["Type"]
),
"None" => Dhall::Forall.new(
var: "A",
type: Dhall::Variable["Type"],
body: Dhall::Application.new(
function: Dhall::Variable["Optional"],
argument: Dhall::Variable["A"]
)
)
}.freeze
def annotate(context)
raise TypeError, "Sort has no Type, Kind, or Sort" if @var.name == "Sort"
Dhall::TypeAnnotation.new(
value: @var,
type: BUILTIN.fetch(@var.name) { context.fetch(@var) }
)
end
end
class Literal
def initialize(lit)
@lit = lit
@type = Dhall::Variable[lit.class.name.split(/::/).last]
end
def annotate(*)
Dhall::TypeAnnotation.new(
value: @lit,
type: @type
)
end
end
class TextLiteral
def initialize(lit)
@lit = lit
end
def annotate(context)
chunks = @lit.chunks.map do |c|
if c.is_a?(Dhall::Text)
c
else
annotated = TypeChecker.for(c).annotate(context)
if annotated.type != Dhall::Variable["Text"]
raise TypeError, "Cannot interpolate non-Text: " \
"#{annotated.type}"
end
annotated
end
end
Dhall::TypeAnnotation.new(
value: @lit.with(chunks: chunks),
type: Dhall::Variable["Text"]
)
end
end
class If
def initialize(expr)
@expr = expr
@predicate = TypeChecker.for(expr.predicate)
@then = TypeChecker.for(expr.then)
@else = TypeChecker.for(expr.else)
end
def annotate(context)
annotated_predicate = @predicate.annotate(context)
if annotated_predicate.type != Dhall::Variable["Bool"]
raise TypeError, "If must have a predicate of type Bool"
end
annotated_then = @then.annotate(context)
then_type_type = TypeChecker.for(annotated_then.type)
.annotate(context).type
if then_type_type != Dhall::Variable["Type"]
raise TypeError, "If branches must have types of type Type"
end
annotated_else = @else.annotate(context)
if annotated_then.type == annotated_else.type
Dhall::TypeAnnotation.new(
value: @expr.with(
predicate: annotated_predicate,
then: annotated_then,
else: annotated_else
),
type: annotated_then.type
)
else
raise TypeError, "If branches have mismatched types: " \
"#{annotated_then.type}, #{annotated_else.type}"
end
end
end
class Operator
def initialize(expr, type)
@expr = expr
@lhs = TypeChecker.for(expr.lhs)
@rhs = TypeChecker.for(expr.rhs)
@type = type
end
def annotate(context)
annotated_lhs = @lhs.annotate(context)
annotated_rhs = @rhs.annotate(context)
types = [annotated_lhs.type, annotated_rhs.type]
if types.any? { |t| t != @type }
raise TypeError, "Operator arguments not #{@type}: #{types}"
end
Dhall::TypeAnnotation.new(
value: @expr.with(lhs: annotated_lhs, rhs: annotated_rhs),
type: @type
)
end
end
class OperatorListConcatenate
def initialize(expr)
@expr = expr
@lhs = TypeChecker.for(expr.lhs)
@rhs = TypeChecker.for(expr.rhs)
end
def annotate(context)
annotated_lhs = @lhs.annotate(context)
annotated_rhs = @rhs.annotate(context)
types = [annotated_lhs.type, annotated_rhs.type]
valid_types = types.all? do |t|
t.is_a?(Dhall::Application) &&
t.function == Dhall::Variable["List"]
end
raise TypeError, "Operator arguments not List: #{types}" unless valid_types
unless annotated_lhs.type == annotated_rhs.type
raise TypeError, "Operator arguments do not match: #{types}"
end
Dhall::TypeAnnotation.new(
value: @expr.with(lhs: annotated_lhs, rhs: annotated_rhs),
type: annotated_lhs.type
)
end
end
class OperatorRecursiveRecordMerge
def initialize(expr)
@expr = expr
@lhs = TypeChecker.for(expr.lhs)
@rhs = TypeChecker.for(expr.rhs)
end
def annotate(context)
annotated_lhs = @lhs.annotate(context)
annotated_rhs = @rhs.annotate(context)
type = annotated_lhs.type.deep_merge_type(annotated_rhs.type)
unless type.is_a?(Dhall::RecordType)
raise TypeError, "RecursiveRecordMerge got #{type}"
end
# Annotate to sanity check
TypeChecker.for(type).annotate(context)
Dhall::TypeAnnotation.new(
value: @expr.with(lhs: annotated_lhs, rhs: annotated_rhs),
type: type
)
end
end
class OperatorRightBiasedRecordMerge
def initialize(expr)
@expr = expr
@lhs = TypeChecker.for(expr.lhs)
@rhs = TypeChecker.for(expr.rhs)
end
def annotate(context)
annotated_lhs = @lhs.annotate(context)
annotated_rhs = @rhs.annotate(context)
unless annotated_lhs.type.is_a?(Dhall::RecordType)
raise TypeError, "RecursiveRecordMerge got #{annotated_lhs.type}"
end
unless annotated_rhs.type.is_a?(Dhall::RecordType)
raise TypeError, "RecursiveRecordMerge got #{annotated_rhs.type}"
end
lkind = TypeChecker.for(annotated_lhs.type).annotate(context).type
rkind = TypeChecker.for(annotated_rhs.type).annotate(context).type
if lkind != rkind
raise TypeError, "RecursiveRecordMerge got mixed kinds: " \
"#{lkind}, #{rkind}"
end
type = annotated_lhs.type.merge_type(annotated_rhs.type)
# Annotate to sanity check
TypeChecker.for(type).annotate(context)
Dhall::TypeAnnotation.new(
value: @expr.with(lhs: annotated_lhs, rhs: annotated_rhs),
type: type
)
end
end
class OperatorRecursiveRecordTypeMerge
def initialize(expr)
@expr = expr
@lhs = TypeChecker.for(expr.lhs)
@rhs = TypeChecker.for(expr.rhs)
end
def annotate(context)
annotated_lhs = @lhs.annotate(context)
annotated_rhs = @rhs.annotate(context)
if annotated_lhs.type != annotated_rhs.type
raise TypeError, "RecursiveRecordTypeMerge mixed kinds: " \
"#{annotated_lhs.type}, #{annotated_rhs.type}"
end
type = @expr.lhs.deep_merge_type(@expr.rhs)
unless type.is_a?(Dhall::RecordType)
raise TypeError, "RecursiveRecordMerge got #{type}"
end
# Annotate to sanity check
TypeChecker.for(type).annotate(context)
Dhall::TypeAnnotation.new(
value: @expr,
type: annotated_lhs.type
)
end
end
class EmptyList
def initialize(expr)
@expr = expr
end
def annotate(context)
TypeChecker.assert_type @expr.element_type, Dhall::Variable["Type"],
"EmptyList element type not of type Type",
context: context
@expr
end
end
class List
def initialize(list)
@list = list
end
class AnnotatedList
def initialize(alist)
@alist = alist
end
def list
@alist.with(element_type: element_type)
end
def element_type
@alist.first.value&.type || @alist.element_type
end
def element_types
@alist.to_a.map(&:type)
end
end
def annotate(context)
alist = AnnotatedList.new(@list.map(type: @list.element_type) { |el|
TypeChecker.for(el).annotate(context)
})
TypeChecker.assert alist.element_types,
Util::ArrayOf.new(alist.element_type),
"Non-homogenous List"
TypeChecker.assert_type alist.element_type, Dhall::Variable["Type"],
"List type not of type Type", context: context
alist.list
end
end
class OptionalNone
def initialize(expr)
@expr = expr
end
def annotate(context)
TypeChecker.assert(
TypeChecker.for(@expr.value_type).annotate(context).type,
Dhall::Variable["Type"],
"OptionalNone element type not of type Type"
)
@expr
end
end
class Optional
def initialize(some)
@some = some
end
def annotate(context)
asome = @some.map(type: @some.value_type) do |el|
TypeChecker.for(el).annotate(context)
end
some = asome.with(value_type: asome.value.type)
type_type = TypeChecker.for(some.value_type).annotate(context).type
TypeChecker.assert type_type, Dhall::Variable["Type"],
"Some type not of type Type, was: #{type_type}"
some
end
end
class EmptyAnonymousType
def initialize(expr)
@expr = expr
end
def annotate(*)
Dhall::TypeAnnotation.new(
value: @expr,
type: Dhall::Variable["Type"]
)
end
end
class AnonymousType
def initialize(type)
@type = type
end
def annotate(context)
kinds = @type.record.values.map do |mtype|
TypeChecker.for(mtype).annotate(context).type
end
TypeChecker.assert (kinds - KINDS), [],
"AnonymousType field kind not one of #{KINDS}"
TypeChecker.assert kinds, Util::ArrayAllTheSame,
"AnonymousType field kinds not all the same"
type = kinds.first || KINDS.first
Dhall::TypeAnnotation.new(value: @type, type: type)
end
end
class EmptyRecord
def initialize(expr)
@expr = expr
end
def annotate(*)
Dhall::TypeAnnotation.new(
value: @expr,
type: Dhall::EmptyRecordType.new
)
end
end
class Record
def initialize(record)
@record = record
end
def annotate(context)
arecord = @record.map do |k, v|
[k, TypeChecker.for(v).annotate(context)]
end
Dhall::TypeAnnotation.new(
value: arecord,
type: TypeChecker.for(Dhall::RecordType.for(Hash[
arecord.record.map { |k, v| [k, v.type] }
])).annotate(context).value
)
end
end
class RecordSelection
def initialize(selection)
@selection = selection
@record = selection.record
@selector = selection.selector
end
class Selector
def self.for(annotated_record)
if annotated_record.type == Dhall::Variable["Type"]
TypeSelector.new(annotated_record.value)
elsif annotated_record.type.class == Dhall::RecordType
new(annotated_record.type)
else
raise TypeError, "RecordSelection on #{annotated_record.type}"
end
end
def initialize(type)
@fetch_from = type.record
end
def select(selector)
@fetch_from.fetch(selector) do
raise TypeError, "#{@fetch_from} has no field #{@selector}"
end
end
end
class TypeSelector < Selector
def initialize(union)
normalized = union.normalize
TypeChecker.assert normalized, Dhall::UnionType,
"RecordSelection on #{normalized}"
@fetch_from = normalized.constructor_types
end
end
def annotate(context)
arecord = TypeChecker.for(@record).annotate(context)
selector = Selector.for(arecord)
Dhall::TypeAnnotation.new(
value: @selection.with(record: arecord),
type: selector.select(@selector)
)
end
end
class RecordProjection
def initialize(projection)
@projection = projection
@record = TypeChecker.for(projection.record)
@selectors = projection.selectors
end
def annotate(context)
arecord = @record.annotate(context)
TypeChecker.assert arecord.type.class.name, "Dhall::RecordType",
"RecordProjection on #{arecord.type}"
slice = arecord.type.slice(@selectors)
TypeChecker.assert slice.keys, @selectors,
"#{arecord.type} missing one of: #{@selectors}"
Dhall::TypeAnnotation.new(
value: @projection.with(record: arecord),
type: slice
)
end
end
class Union
def initialize(union)
@union = union
@value = TypeChecker.for(union.value)
end
def annotate(context)
annotated_value = @value.annotate(context)
type = Dhall::UnionType.new(
alternatives: { @union.tag => annotated_value.type }
).merge(@union.alternatives)
# Annotate to sanity check
TypeChecker.for(type).annotate(context)
Dhall::TypeAnnotation.new(
value: @union.with(value: annotated_value),
type: type
)
end
end
class Merge
def initialize(merge)
@merge = merge
@record = TypeChecker.for(merge.record)
@union = TypeChecker.for(merge.input)
end
class Handlers
def initialize(annotation)
@type = annotation.type
TypeChecker.assert @type, Dhall::RecordType,
"Merge expected Record got: #{@type}"
@type.record.values.each do |htype|
TypeChecker.assert htype, Dhall::Forall,
"Merge handlers must all be functions"
end
end
def output_type(output_annotation=nil)
@type.record.values.reduce(output_annotation) do |type_acc, htype|
if type_acc && htype.body.normalize != type_acc.normalize
raise TypeError, "Handler output types must all match"
end
htype.body.shift(-1, htype.var, 0)
end
end
def keys
@type.record.keys
end
def fetch(k)
@type.record.fetch(k) do
raise TypeError, "No merge handler for alternative: #{k}"
end
end
end
class AnnotatedMerge
def initialize(merge:, record:, input:)
@merge = merge.with(record: record, input: input)
@handlers = Handlers.new(record)
@record = record
@union = input
TypeChecker.assert @union.type, Dhall::UnionType,
"Merge expected Union got: #{@union.type}"
assert_union_and_handlers_match
end
def annotation
Dhall::TypeAnnotation.new(
value: @merge,
type: type
)
end
def type
@type ||= @handlers.output_type(@merge.type)
end
def assert_kind(context)
kind = TypeChecker.for(type).annotate(context).type
TypeChecker.assert(
kind,
Dhall::Variable["Type"],
"Merge must have kind Type"
)
kind
end
def assert_union_and_handlers_match
extras = @handlers.keys - @union.type.alternatives.keys
TypeChecker.assert extras, [],
"Merge handlers unknown alternatives: #{extras}"
@union.type.alternatives.each do |k, atype|
TypeChecker.assert(
@handlers.fetch(k).type,
atype,
"Handler argument does not match alternative type: #{atype}"
)
end
end
end
def annotate(context)
amerge = AnnotatedMerge.new(
merge: @merge,
record: @record.annotate(context),
input: @union.annotate(context)
)
amerge.assert_kind(context)
amerge.annotation
end
end
class Forall
def initialize(expr)
@expr = expr
@var = expr.var
@var_type = expr.type
@input = TypeChecker.for(expr.type)
@output = TypeChecker.for(expr.body)
end
module FunctionKind
def self.for(inkind, outkind)
if inkind.nil? || outkind.nil?
raise TypeError, "FunctionType part of this is a term"
end
raise TypeError, "Dependent types are not allowed" if outkind > inkind
if outkind.zero?
Term.new
else
Polymorphic.new(inkind, outkind)
end
end
class Term
def kind
KINDS.first
end
end
class Polymorphic
def initialize(inkind, outkind)
@inkind = inkind
@outkind = outkind
end
def kind
KINDS[[@outkind, @inkind].max]
end
end
end
def annotate(context)
inkind = KINDS.index(@input.annotate(context).type)
outkind = KINDS.index(@output.annotate(context.add(@expr)).type)
Dhall::TypeAnnotation.new(
value: @expr,
type: FunctionKind.for(inkind, outkind).kind
)
end
end
class Function
def initialize(func)
@func = func
@type = Dhall::Forall.new(
var: func.var,
type: func.type,
body: Dhall::Variable["UNKNOWN"]
)
@output = TypeChecker.for(func.body)
end
def annotate(context)
abody = @output.annotate(context.add(@type))
Dhall::TypeAnnotation.new(
value: @func.with(body: abody),
type: TypeChecker.for(
@type.with(body: abody.type)
).annotate(context).value
)
end
end
class Application
def initialize(app)
@app = app
@func = TypeChecker.for(app.function)
@arg = app.argument
end
def annotate(context)
afunc = @func.annotate(context)
TypeChecker.assert afunc.type, Dhall::Forall,
"Application LHS is not a function"
aarg = TypeChecker.for(
Dhall::TypeAnnotation.new(value: @arg, type: afunc.type.type)
).annotate(context)
Dhall::TypeAnnotation.new(
value: @app.with(function: afunc, argument: aarg),
type: afunc.type.call(aarg.value)
)
end
end
class LetBlock
def self.for(letblock)
if letblock.lets.first.type
LetBlockAnnotated.new(letblock)
else
LetBlock.new(letblock)
end
end
def initialize(letblock)
@letblock = letblock.unflatten
@let = @letblock.lets.first
end
def annotate(context)
alet = @let.with(type: assign_type(context))
type = TypeChecker.for(@letblock.eliminate).annotate(context).type
abody = Dhall::TypeAnnotation.new(value: @letblock.body, type: type)
Dhall::TypeAnnotation.new(
value: @letblock.with(lets: [alet], body: abody),
type: type
)
end
protected
def assign_type(context)
TypeChecker.for(@let.assign).annotate(context).type
end
end
class LetBlockAnnotated < LetBlock
protected
def assign_type(context)
TypeChecker.for(
Dhall::TypeAnnotation.new(
value: @let.assign,
type: @let.type
)
).annotate(context).type
end
end
class TypeAnnotation
def initialize(expr)
@expr = expr
end
def annotate(context)
redo_annotation = TypeChecker.for(@expr.value).annotate(context)
if redo_annotation.type.normalize == @expr.type.normalize
redo_annotation
else
raise TypeError, "TypeAnnotation does not match: " \
"#{@expr.type}, #{redo_annotation.type}"
end
end
end
class Builtin
def initialize(builtin)
@expr = builtin
@name = builtin.as_json
end
TYPES = {
"Natural/build" => Dhall::Forall.of_arguments(
Dhall::Forall.new(
var: "natural",
type: Dhall::Variable["Type"],
body: Dhall::Forall.new(
var: "succ",
type: Dhall::Forall.of_arguments(
Dhall::Variable["natural"],
body: Dhall::Variable["natural"]
),
body: Dhall::Forall.new(
var: "zero",
type: Dhall::Variable["natural"],
body: Dhall::Variable["natural"]
)
)
),
body: Dhall::Variable["Natural"]
),
"Natural/fold" => Dhall::Forall.of_arguments(
Dhall::Variable["Natural"],
body: Dhall::Forall.new(
var: "natural",
type: Dhall::Variable["Type"],
body: Dhall::Forall.new(
var: "succ",
type: Dhall::Forall.of_arguments(
Dhall::Variable["natural"],
body: Dhall::Variable["natural"]
),
body: Dhall::Forall.new(
var: "zero",
type: Dhall::Variable["natural"],
body: Dhall::Variable["natural"]
)
)
)
),
"Natural/isZero" => Dhall::Forall.of_arguments(
Dhall::Variable["Natural"],
body: Dhall::Variable["Bool"]
),
"Natural/even" => Dhall::Forall.of_arguments(
Dhall::Variable["Natural"],
body: Dhall::Variable["Bool"]
),
"Natural/odd" => Dhall::Forall.of_arguments(
Dhall::Variable["Natural"],
body: Dhall::Variable["Bool"]
),
"Natural/toInteger" => Dhall::Forall.of_arguments(
Dhall::Variable["Natural"],
body: Dhall::Variable["Integer"]
),
"Natural/show" => Dhall::Forall.of_arguments(
Dhall::Variable["Natural"],
body: Dhall::Variable["Text"]
),
"Text/show" => Dhall::Forall.of_arguments(
Dhall::Variable["Text"],
body: Dhall::Variable["Text"]
),
"List/build" => Dhall::Forall.new(
var: "a",
type: Dhall::Variable["Type"],
body: Dhall::Forall.of_arguments(
Dhall::Forall.new(
var: "list",
type: Dhall::Variable["Type"],
body: Dhall::Forall.new(
var: "cons",
type: Dhall::Forall.of_arguments(
Dhall::Variable["a"],
Dhall::Variable["list"],
body: Dhall::Variable["list"]
),
body: Dhall::Forall.new(
var: "nil",
type: Dhall::Variable["list"],
body: Dhall::Variable["list"]
)
)
),
body: Dhall::Application.new(
function: Dhall::Variable["List"],
argument: Dhall::Variable["a"]
)
)
),
"List/fold" => Dhall::Forall.new(
var: "a",
type: Dhall::Variable["Type"],
body: Dhall::Forall.of_arguments(
Dhall::Application.new(
function: Dhall::Variable["List"],
argument: Dhall::Variable["a"]
),
body: Dhall::Forall.new(
var: "list",
type: Dhall::Variable["Type"],
body: Dhall::Forall.new(
var: "cons",
type: Dhall::Forall.of_arguments(
Dhall::Variable["a"],
Dhall::Variable["list"],
body: Dhall::Variable["list"]
),
body: Dhall::Forall.new(
var: "nil",
type: Dhall::Variable["list"],
body: Dhall::Variable["list"]
)
)
)
)
),
"List/length" => Dhall::Forall.new(
var: "a",
type: Dhall::Variable["Type"],
body: Dhall::Forall.of_arguments(
Dhall::Application.new(
function: Dhall::Variable["List"],
argument: Dhall::Variable["a"]
),
body: Dhall::Variable["Natural"]
)
),
"List/head" => Dhall::Forall.new(
var: "a",
type: Dhall::Variable["Type"],
body: Dhall::Forall.of_arguments(
Dhall::Application.new(
function: Dhall::Variable["List"],
argument: Dhall::Variable["a"]
),
body: Dhall::Application.new(
function: Dhall::Variable["Optional"],
argument: Dhall::Variable["a"]
)
)
),
"List/last" => Dhall::Forall.new(
var: "a",
type: Dhall::Variable["Type"],
body: Dhall::Forall.of_arguments(
Dhall::Application.new(
function: Dhall::Variable["List"],
argument: Dhall::Variable["a"]
),
body: Dhall::Application.new(
function: Dhall::Variable["Optional"],
argument: Dhall::Variable["a"]
)
)
),
"List/indexed" => Dhall::Forall.new(
var: "a",
type: Dhall::Variable["Type"],
body: Dhall::Forall.of_arguments(
Dhall::Application.new(
function: Dhall::Variable["List"],
argument: Dhall::Variable["a"]
),
body: Dhall::Application.new(
function: Dhall::Variable["List"],
argument: Dhall::RecordType.new(
record: {
"index" => Dhall::Variable["Natural"],
"value" => Dhall::Variable["a"]
}
)
)
)
),
"List/reverse" => Dhall::Forall.new(
var: "a",
type: Dhall::Variable["Type"],
body: Dhall::Forall.of_arguments(
Dhall::Application.new(
function: Dhall::Variable["List"],
argument: Dhall::Variable["a"]
),
body: Dhall::Application.new(
function: Dhall::Variable["List"],
argument: Dhall::Variable["a"]
)
)
),
"Optional/fold" => Dhall::Forall.new(
var: "a",
type: Dhall::Variable["Type"],
body: Dhall::Forall.of_arguments(
Dhall::Application.new(
function: Dhall::Variable["Optional"],
argument: Dhall::Variable["a"]
),
body: Dhall::Forall.new(
var: "optional",
type: Dhall::Variable["Type"],
body: Dhall::Forall.new(
var: "just",
type: Dhall::Forall.of_arguments(
Dhall::Variable["a"],
body: Dhall::Variable["optional"]
),
body: Dhall::Forall.new(
var: "nothing",
type: Dhall::Variable["optional"],
body: Dhall::Variable["optional"]
)
)
)
)
),
"Optional/build" => Dhall::Forall.new(
var: "a",
type: Dhall::Variable["Type"],
body: Dhall::Forall.of_arguments(
Dhall::Forall.new(
var: "optional",
type: Dhall::Variable["Type"],
body: Dhall::Forall.new(
var: "just",
type: Dhall::Forall.of_arguments(
Dhall::Variable["a"],
body: Dhall::Variable["optional"]
),
body: Dhall::Forall.new(
var: "nothing",
type: Dhall::Variable["optional"],
body: Dhall::Variable["optional"]
)
)
),
body: Dhall::Application.new(
function: Dhall::Variable["Optional"],
argument: Dhall::Variable["a"]
)
)
),
"Integer/show" => Dhall::Forall.of_arguments(
Dhall::Variable["Integer"],
body: Dhall::Variable["Text"]
),
"Integer/toDouble" => Dhall::Forall.of_arguments(
Dhall::Variable["Integer"],
body: Dhall::Variable["Double"]
),
"Double/show" => Dhall::Forall.of_arguments(
Dhall::Variable["Double"],
body: Dhall::Variable["Text"]
)
}.freeze
def annotate(*)
Dhall::TypeAnnotation.new(
value: @expr,
type: TYPES.fetch(@name) do
raise TypeError, "Unknown Builtin #{@name}"
end
)
end
end
end
end