From 6e28edfe220d024ceed9cc01bc43b1aede670681 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Tue, 30 Apr 2019 20:17:54 -0500 Subject: [PATCH] Split Union and Enum Stop putting special case conditionals for Union#value.nil? everywhere and just split the classes. --- lib/dhall/as_dhall.rb | 9 +++-- lib/dhall/ast.rb | 87 ++++++++++++++++++++++++++-------------- lib/dhall/typecheck.rb | 19 +++++++++ test/test_as_dhall.rb | 3 +- test/test_typechecker.rb | 14 +++++++ 5 files changed, 96 insertions(+), 36 deletions(-) diff --git a/lib/dhall/as_dhall.rb b/lib/dhall/as_dhall.rb index 53ae133..94cd20e 100644 --- a/lib/dhall/as_dhall.rb +++ b/lib/dhall/as_dhall.rb @@ -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 diff --git a/lib/dhall/ast.rb b/lib/dhall/ast.rb index 451d6cf..c96659a 100644 --- a/lib/dhall/ast.rb +++ b/lib/dhall/ast.rb @@ -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 diff --git a/lib/dhall/typecheck.rb b/lib/dhall/typecheck.rb index f029228..2bb0776 100644 --- a/lib/dhall/typecheck.rb +++ b/lib/dhall/typecheck.rb @@ -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 diff --git a/test/test_as_dhall.rb b/test/test_as_dhall.rb index 341104d..2ccb723 100644 --- a/test/test_as_dhall.rb +++ b/test/test_as_dhall.rb @@ -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 diff --git a/test/test_typechecker.rb b/test/test_typechecker.rb index ae07e84..213c3e8 100644 --- a/test/test_typechecker.rb +++ b/test/test_typechecker.rb @@ -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 -- 2.34.5