M lib/dhall/as_dhall.rb => lib/dhall/as_dhall.rb +5 -4
@@ 24,9 24,11 @@ module Dhall
end
def self.union_of(values_and_types)
- z = [UnionType.new(alternatives: {}), []]
- values_and_types.reduce(z) do |(ut, tags), (v, t)|
+ values_and_types.reduce([UnionType.new, []]) do |(ut, tags), (v, t)|
tag = tag_for(v, t)
+ if t.is_a?(UnionType) && t.alternatives.length == 1
+ tag, t = t.alternatives.to_a.first
+ end
[
ut.merge(UnionType.new(alternatives: { tag => t })),
tags + [tag]
@@ 46,9 48,8 @@ module Dhall
refine ::Symbol do
def as_dhall
- Dhall::Union.new(
+ Dhall::Enum.new(
tag: to_s,
- value: nil,
alternatives: Dhall::UnionType.new(alternatives: {})
)
end
M lib/dhall/ast.rb => lib/dhall/ast.rb +57 -30
@@ 764,9 764,22 @@ module Dhall
class UnionType < Expression
include(ValueSemantics.for_attributes do
- alternatives Util::HashOf.new(::String, Either(Expression, nil))
+ alternatives Util::HashOf.new(::String, Either(Expression, nil)), default: {}
end)
+ def empty?
+ alternatives.empty?
+ end
+
+ def [](k)
+ alternatives.fetch(k)
+ end
+
+ def without(key)
+ key = key.to_s
+ with(alternatives: alternatives.reject { |k, _| k == key })
+ end
+
def record
alternatives
end
@@ 817,31 830,28 @@ module Dhall
class Union < Expression
include(ValueSemantics.for_attributes do
tag ::String
- value Either(Expression, nil)
+ value Expression
alternatives UnionType
end)
def self.from(alts, tag, value)
- new(
- tag: tag,
- value: value && TypeAnnotation.new(
- value: value,
- type: alts.alternatives[tag]
- ),
- alternatives: alts.with(
- alternatives: alts.alternatives.reject { |alt, _| alt == tag }
+ if value.nil?
+ Enum.new(tag: tag, alternatives: alts)
+ else
+ new(
+ tag: tag,
+ value: TypeAnnotation.new(value: value, type: alts[tag]),
+ alternatives: alts.without(tag)
)
- )
+ end
end
def to_s
- value.nil? ? tag : extract.to_s
+ extract.to_s
end
def extract
- if value.nil?
- tag.to_sym
- elsif value.is_a?(TypeAnnotation)
+ if value.is_a?(TypeAnnotation)
value.value
else
value
@@ 851,12 861,8 @@ module Dhall
def reduce(handlers)
handlers = handlers.to_h
handler = handlers.fetch(tag.to_sym) { handlers.fetch(tag) }
- if value.nil?
- handler
- else
- (handler.respond_to?(:to_proc) ? handler.to_proc : handler)
- .call(extract)
- end
+ (handler.respond_to?(:to_proc) ? handler.to_proc : handler)
+ .call(extract)
end
def selection_syntax
@@ 869,18 875,14 @@ module Dhall
end
def syntax
- if value.nil?
- selection_syntax
- else
- Application.new(
- function: selection_syntax,
- argument: value.is_a?(TypeAnnotation) ? value.value : value
- )
- end
+ Application.new(
+ function: selection_syntax,
+ argument: value.is_a?(TypeAnnotation) ? value.value : value
+ )
end
def as_json
- if value.nil? || value.respond_to?(:type)
+ if value.respond_to?(:type)
syntax.as_json
else
[12, tag, value&.as_json, alternatives.as_json.last]
@@ 888,6 890,31 @@ module Dhall
end
end
+ class Enum < Union
+ include(ValueSemantics.for_attributes do
+ tag ::String
+ alternatives UnionType
+ end)
+
+ def reduce(handlers)
+ handlers = handlers.to_h
+ handler = handlers.fetch(tag.to_sym) { handlers.fetch(tag) }
+ handler
+ end
+
+ def to_s
+ tag
+ end
+
+ def extract
+ tag.to_sym
+ end
+
+ def as_json
+ selection_syntax.as_json
+ end
+ end
+
class If < Expression
include(ValueSemantics.for_attributes do
predicate Expression
M lib/dhall/typecheck.rb => lib/dhall/typecheck.rb +19 -0
@@ 604,6 604,25 @@ module Dhall
end
end
+ class Enum
+ TypeChecker.register self, Dhall::Enum
+
+ def initialize(enum)
+ @enum = enum
+ end
+
+ def annotate(context)
+ type = Dhall::UnionType.new(
+ alternatives: { @enum.tag => nil }
+ ).merge(@enum.alternatives)
+
+ # Annotate to sanity check
+ TypeChecker.for(type).annotate(context)
+
+ Dhall::TypeAnnotation.new(value: @enum, type: type)
+ end
+ end
+
class Union
TypeChecker.register self, Dhall::Union
M test/test_as_dhall.rb => test/test_as_dhall.rb +1 -2
@@ 27,9 27,8 @@ class TestAsDhall < Minitest::Test
def test_symbol
assert_equal(
- Dhall::Union.new(
+ Dhall::Enum.new(
tag: "hai",
- value: nil,
alternatives: Dhall::UnionType.new(alternatives: {})
),
:hai.as_dhall
M test/test_typechecker.rb => test/test_typechecker.rb +14 -0
@@ 112,4 112,18 @@ class TestTypechecker < Minitest::Test
)
end
end
+
+ def test_enum
+ union = Dhall::Enum.new(
+ tag: "red",
+ alternatives: Dhall::UnionType.new(alternatives: {})
+ )
+
+ assert_equal(
+ Dhall::UnionType.new(alternatives: { "red" => nil }),
+ Dhall::TypeChecker.for(union).annotate(
+ Dhall::TypeChecker::Context.new
+ ).type
+ )
+ end
end