From d5ff7d89be76f8f785a0e415dfe07ec868a33681 Mon Sep 17 00:00:00 2001
From: Lou Knauer <lou.knauer@gmx.de>
Date: Tue, 5 Mar 2019 12:01:45 +0100
Subject: [PATCH] removed Value class and adapted stdlib

---
 lib/stdlib.rb                                 | 177 ++++++++----------
 lib/types.rb                                  | 111 +++--------
 lib/utils.rb                                  |   4 +
 examples.dhallish => tests/examples.dhallish  |   9 +-
 .../examples_expected.json                    |   2 +-
 tests/test.rb                                 |   4 +-
 6 files changed, 121 insertions(+), 186 deletions(-)
 rename examples.dhallish => tests/examples.dhallish (92%)
 rename examples.expected.json => tests/examples_expected.json (67%)

diff --git a/lib/stdlib.rb b/lib/stdlib.rb
index 02501b8..edbab7a 100644
--- a/lib/stdlib.rb
+++ b/lib/stdlib.rb
@@ -22,12 +22,12 @@ module Dhallish
 	def fill_context(globalctx, types)
 
 		# Types:
-		globalctx["Natural"] = Value.new(Types::Natural, Types::Type.new)
-		globalctx["Integer"] = Value.new(Types::Integer, Types::Type.new)
-		globalctx["Double"] = Value.new(Types::Double, Types::Type.new)
-		globalctx["Bool"] = Value.new(Types::Bool, Types::Type.new)
-		globalctx["Text"] = Value.new(Types::Text, Types::Type.new)
-		globalctx["Type"] = Value.new(Types::Type.new, Types::Type.new)
+		globalctx["Natural"] = Types::Natural
+		globalctx["Integer"] = Types::Integer
+		globalctx["Double"] = Types::Double
+		globalctx["Bool"] = Types::Bool
+		globalctx["Text"] = Types::Text
+		globalctx["Type"] = Types::Type.new
 
 		types["Natural"] = Types::Type.new(Types::Natural)
 		types["Integer"] = Types::Type.new(Types::Integer)
@@ -37,58 +37,53 @@ module Dhallish
 		types["Type"] = Types::Type.new(Types::Type.new)
 
 		# Shows:
-		natShow = BuiltinFunction.new { |arg| Value.new arg.val.to_s, Types::Text }
-		globalctx["Natural/show"] = Value.new(natShow, Types::Function.new(Types::Natural, Types::Text))
+		globalctx["Natural/show"] = BuiltinFunction.new { |arg| arg.to_s }
 		types["Natural/show"] = Types::Function.new(Types::Natural, Types::Text)
 
-		intShow = BuiltinFunction.new { |arg| Value.new arg.val.to_s, Types::Text }
-		globalctx["Integer/show"] = Value.new(intShow, Types::Function.new(Types::Integer, Types::Text))
+		globalctx["Integer/show"] = BuiltinFunction.new { |arg| arg.to_s }
 		types["Integer/show"] = Types::Function.new(Types::Integer, Types::Text)
 
-		doubleShow = BuiltinFunction.new { |arg| Value.new arg.val.to_s, Types::Text }
-		globalctx["Double/show"] = Value.new(doubleShow, Types::Function.new(Types::Double, Types::Text))
+		globalctx["Double/show"] =  BuiltinFunction.new { |arg| arg.to_s }
 		types["Double/show"] = Types::Function.new(Types::Double, Types::Text)
 
 		# Casts:
-		natToInt = BuiltinFunction.new { |arg| Value.new arg.val, Types::Integer }
-		globalctx["Natural/toInteger"] = Value.new(natToInt, Types::Function.new(Types::Natural, Types::Integer))
+		natToInt = BuiltinFunction.new { |arg| arg }
+		globalctx["Natural/toInteger"] = natToInt
 		types["Natural/toInteger"] = Types::Function.new(Types::Natural, Types::Integer)
 
-		natToDou = BuiltinFunction.new { |arg| Value.new arg.val.to_f, Types::Double }
-		globalctx["Natural/toDouble"] = Value.new(natToDou, Types::Function.new(Types::Natural, Types::Double))
+		natToDou = BuiltinFunction.new { |arg| arg.to_f }
+		globalctx["Natural/toDouble"] = natToDou
 		types["Natural/toDouble"] = Types::Function.new(Types::Natural, Types::Double)
 
 
-		intToNat = BuiltinFunction.new { |arg| Value.new arg.val.abs, Types::Natural }
-		globalctx["Integer/toNatural"] = Value.new(intToNat, Types::Function.new(Types::Integer, Types::Natural))
+		intToNat = BuiltinFunction.new { |arg| arg.abs  }
+		globalctx["Integer/toNatural"] = intToNat
 		types["Integer/toNatural"] = Types::Function.new(Types::Integer, Types::Natural)
 
-		intToDou = BuiltinFunction.new { |arg| Value.new arg.val.to_f, Types::Double }
-		globalctx["Integer/toDouble"] = Value.new(intToDou, Types::Function.new(Types::Integer, Types::Double))
+		intToDou = BuiltinFunction.new { |arg| arg.to_f }
+		globalctx["Integer/toDouble"] = intToDou
 		types["Integer/toDouble"] = Types::Function.new(Types::Integer, Types::Double)
 
 
-		douToNat = BuiltinFunction.new { |arg| Value.new arg.val.round.abs, Types::Natural }
-		globalctx["Double/toNatural"] = Value.new(douToNat, Types::Function.new(Types::Double, Types::Natural))
+		douToNat = BuiltinFunction.new { |arg| arg.round.abs }
+		globalctx["Double/toNatural"] = douToNat
 		types["Double/toNatural"] = Types::Function.new(Types::Double, Types::Natural)
 
-		douToInt = BuiltinFunction.new { |arg| Value.new arg.val.round, Types::Integer }
-		globalctx["Double/toInteger"] = Value.new(douToInt, Types::Function.new(Types::Double, Types::Integer))
+		douToInt = BuiltinFunction.new { |arg| arg.round }
+		globalctx["Double/toInteger"] = douToInt
 		types["Double/toInteger"] = Types::Function.new(Types::Double, Types::Integer)
 
 
 		# Lists:
-		globalctx["List"] = Value.new(BuiltinFunction.new { |a|
-			Value.new(Types::List.new(a.val), Types::Type.new)
-		}, make_fn_type([Types::Type.new, :a], Types::Type.new(Types::List.new(Types::Unresolved.new(:a)))))
+		globalctx["List"] = BuiltinFunction.new { |a| Types::List.new(a) }
 		types["List"] = make_fn_type([Types::Type.new, :a], Types::Type.new(Types::List.new(Types::Unresolved.new(:a))))
 
 		list_length_type = make_fn_type([Types::Type.new(Types::Unresolved.new(:a)), :a], Types::List.new(Types::Unresolved.new(:a)), Types::Natural)
-		globalctx["List/length"] = Value.new(BuiltinFunction.new{ |a|
-			Value.new(BuiltinFunction.new { |list|
-				Value.new(list.val.length, Types::Natural)
-			}, Types::resolve(list_length_type.restype, :a, a.val))
-		}, list_length_type)
+		globalctx["List/length"] = BuiltinFunction.new{ |a|
+			BuiltinFunction.new { |list|
+				list.length
+			}
+		}
 		types["List/length"] = list_length_type
 
 		list_fold_type = make_fn_type(
@@ -97,67 +92,60 @@ module Dhallish
 			[Types::Type.new(Types::Unresolved.new(:b)), :b],
 			make_fn_type(:a, :b, :b),
 			:b, :b)
-		globalctx["List/fold"] = Value.new(BuiltinFunction.new { |a|
-			type = Types::resolve(list_fold_type.restype, :a, a.val)
-			Value.new(BuiltinFunction.new { |list|
-				type = type.restype
-				Value.new(BuiltinFunction.new { |b|
-					type = Types::resolve(type.restype, :b, b.val)
-					Value.new(BuiltinFunction.new { |f|
-						type = type.restype
-						Value.new(BuiltinFunction.new { |x|
-							list.val.reduce(x) { |acc, x| f.call(x).call(acc) }
-						}, type)
-					}, type)
-				}, type)
-			}, type)
-		}, list_fold_type)
+		globalctx["List/fold"] = BuiltinFunction.new { |a|
+			BuiltinFunction.new { |list|
+				BuiltinFunction.new { |b|
+					BuiltinFunction.new { |f|
+						BuiltinFunction.new { |x|
+							list.reduce(x) { |acc, x| f.call(x).call(acc) }
+						}
+					}
+				}
+			}
+		}
 		types["List/fold"] = list_fold_type
 
 		list_head_type = make_fn_type(
 			[Types::Type.new(Types::Unresolved.new(:a)), :a],
 			Types::List.new(Types::Unresolved.new(:a)),
 			Types::Optional.new(Types::Unresolved.new(:a)))
-		globalctx["List/head"] = Value.new(BuiltinFunction.new { |a|
-			type = Types::resolve(list_head_type.restype, :a, a.val)
-			Value.new(BuiltinFunction.new { |list|
-				Value.new(list.val.first, type.restype)
-			}, type)
-		}, list_head_type)
+		globalctx["List/head"] = BuiltinFunction.new { |a|
+			BuiltinFunction.new { |list|
+				list.first
+			}
+		}
 		types["List/head"] = list_head_type
 
 		list_last_type = make_fn_type(
 			[Types::Type.new(Types::Unresolved.new(:a)), :a],
 			Types::List.new(Types::Unresolved.new(:a)),
 			Types::Optional.new(Types::Unresolved.new(:a)))
-		globalctx["List/last"] = Value.new(BuiltinFunction.new { |a|
-			type = Types::resolve(list_last_type.restype, :a, a.val)
-			Value.new(BuiltinFunction.new { |list|
-				Value.new(list.val.last, type.restype)
-			}, type)
-		}, list_last_type)
+		globalctx["List/last"] = BuiltinFunction.new { |a|
+			BuiltinFunction.new { |list|
+				list.last
+			}
+		}
 		types["List/last"] = list_last_type
 
 		list_tail_type = make_fn_type(
 			[Types::Type.new(Types::Unresolved.new(:a)), :a],
 			Types::List.new(Types::Unresolved.new(:a)),
 			Types::List.new(Types::Unresolved.new(:a)))
-		globalctx["List/tail"] = Value.new(BuiltinFunction.new { |a|
-			type = Types::resolve(list_tail_type.restype, :a, a.val)
-			Value.new(BuiltinFunction.new { |list|
-				if list.val.empty?
-					Value.new([], type.restype)
+		globalctx["List/tail"] = BuiltinFunction.new { |a|
+			BuiltinFunction.new { |list|
+				if list.empty?
+					[]
 				else
-					Value.new(list.val[1..list.val.length], type.restype)
+					list[1..list.length]
 				end
-			}, type)
-		}, list_tail_type)
+			}
+		}
 		types["List/tail"] = list_tail_type
 
 		# Optionals:
-		globalctx["Optional"] = Value.new(BuiltinFunction.new { |a|
-			Value.new(Types::Optional.new(a.val), Types::Type.new)
-		}, make_fn_type([Types::Type.new, :a], Types::Type.new(Types::Optional.new(Types::Unresolved.new(:a)))))
+		globalctx["Optional"] = BuiltinFunction.new { |a|
+			Types::Optional.new(a)
+		}
 		types["Optional"] = make_fn_type([Types::Type.new, :a], Types::Type.new(Types::Optional.new(Types::Unresolved.new(:a))))
 
 		optional_fold_type = make_fn_type(
@@ -166,25 +154,21 @@ module Dhallish
 			[Types::Type.new(Types::Unresolved.new(:b)), :b],
 			make_fn_type(:a, :b),
 			:b, :b)
-		globalctx["Optional/fold"] = Value.new(BuiltinFunction.new { |a|
-			type = Types::resolve(optional_fold_type.restype, :a, a.val)
-			Value.new(BuiltinFunction.new { |opt|
-				type = type.restype
-				Value.new(BuiltinFunction.new { |b|
-					type = Types::resolve(type.restype, :b, b.val)
-					Value.new(BuiltinFunction.new { |f|
-						type = type.restype
-						Value.new(BuiltinFunction.new { |x|
-							if opt.val.nil?
+		globalctx["Optional/fold"] = BuiltinFunction.new { |a|
+			BuiltinFunction.new { |opt|
+				BuiltinFunction.new { |b|
+					BuiltinFunction.new { |f|
+						BuiltinFunction.new { |x|
+							if opt.nil?
 								x
 							else
-								f.call(opt.val)
+								f.call(opt)
 							end
-						}, type)
-					}, type)
-				}, type)
-			}, type)
-		}, optional_fold_type)
+						}
+					}
+				}
+			}
+		}
 		types["Optional/fold"] = optional_fold_type
 
 		# Naturals:
@@ -193,20 +177,17 @@ module Dhallish
 			[Types::Type.new(Types::Unresolved.new(:a)), :a],
 			make_fn_type(:a, :a),
 			:a, :a)
-		globalctx["Natural/fold"] = Value.new(BuiltinFunction.new { |n|
-			type = natural_fold_type.restype
-			Value.new(BuiltinFunction.new { |a|
-				type = Types::resolve(type.restype, :a, a.val)
-				Value.new(BuiltinFunction.new { |succ|
-					type = type.restype
-					Value.new(BuiltinFunction.new { |zero|
+		globalctx["Natural/fold"] = BuiltinFunction.new { |n|
+			BuiltinFunction.new { |a|
+				BuiltinFunction.new { |succ|
+					BuiltinFunction.new { |zero|
 						res = zero
-						n.val.times { res = succ.call(res) }
+						n.times { res = succ.call(res) }
 						res
-					}, type)
-				}, type)
-			}, type)
-		}, natural_fold_type)
+					}
+				}
+			}
+		}
 		types["Natural/fold"] = natural_fold_type
 
 	end
diff --git a/lib/types.rb b/lib/types.rb
index fe558f0..ae766cb 100644
--- a/lib/types.rb
+++ b/lib/types.rb
@@ -138,7 +138,7 @@ module Dhallish
 					argtype = orgtype.argtype
 					if orgtype.unres.nil? or orgtype.unres != name
 						restype = resolve(orgtype.restype, name, newtype)
-						argtype = resolve(orgtype.argtype, name, newtype) 
+						argtype = resolve(orgtype.argtype, name, newtype)
 					end
 					Function.new(argtype, restype, orgtype.unres)
 				end
@@ -241,88 +241,27 @@ module Dhallish
 		Numbers = [Natural, Integer, Double]
 	end
 
-	# Can be used as AST-Node.
-	class Value
-		attr_accessor :val
-		attr_accessor :type
-		def initialize(val, type)
-			@val = val
-			@type = type
-		end
-
-		def compute_type(ctx)
-			return @type
-		end
-
-		def to_json()
-			case type
-			when Types::Natural
-				val.to_s
-			when Types::Integer
-				val.to_s
-			when Types::Double
-				val.to_s
-			when Types::Bool
-				if val
-					"true"
-				else
-					"false"
-				end
-			when Types::Text
-				"\"#{val.gsub "\n", "\\n"}\""
-			when Types::Optional
-				if val.nil?
-					"null"
-				else
-					val.to_json
-				end
-			when Types::List
-				"[ #{val.map{ |elm|
-					elm.to_json
-				}.join(", ")} ]"
-			when Types::Record
-				elms = []
-				val.each { |key, val|
-					elms.push("\"#{key}\": #{val.to_json}")
-				}
-				"{ #{elms.join ", "} }"
-			else
-				"{ \"type\": \"#{@type.to_s}\", \"value\": \"#{@val.to_s}\" }"
-			end
-		end
-
-		def evaluate(ctx) self end
-
-		def call(arg)
-			assert("not a function: #{@type}") { @type.is_a? Types::Function }
-			assert("argument type missmatch: #{arg.type} vs. #{@type.argtype}") { arg.type == @type.argtype }
-
-			res = nil
-			if @val.is_a? BuiltinFunction
-				res = @val.block.call arg
-			elsif @val.is_a? Function
-				ctx = Context.new(@val.ctx)
-				ctx[@val.argname] = arg
-				res = @val.ast.evaluate ctx
-				if res.type.is_a? Types::Function and !@type.restype.nil?
-					res.type.restype = @type.restype.restype
-					res.type.unres  = @type.restype.unres
-				end
-			end
-
-			if !@type.restype.nil?
-				if !@type.unres.nil?
-					expected_type = Types::resolve(@type.restype, @type.unres, arg.val)
-					res.type = Types::resolve(res.type, @type.unres, arg.val)
-					assert ("return type mismatch. expected #{expected_type}, actual #{res.type}") { res.type == expected_type }
-				else
-					assert("function return type missmatch: actual type #{res.type}, expected #{@type.restype}") { res.type == @type.restype }
-				end
-			end
-
-			res
+	def to_json(dhallval)
+		case dhallval
+		when Integer
+			dhallval.to_s
+		when Float
+			dhallval.to_s
+		when String
+			 "\"#{escape_str(dhallval)}\""
+		when TrueClass
+			"true"
+		when FalseClass
+			"false"
+		when Array
+			"[#{ dhallval.map{ |val| to_json(val) }.join(", ") }]"
+		when Hash
+			"{#{ dhallval.map{ |key, val| "\"#{escape_str(key)}\": #{ to_json(val) }" }.join(", ") }}"
+		else
+			"\"Dhallish-Internal: #{escape_str(dhallval.inspect)}\""
 		end
 	end
+	module_function :to_json
 
 	# To be used as Dhallish::Value.val for dhall-defined functions
 	class Function
@@ -334,6 +273,12 @@ module Dhallish
 			@ast = ast
 			@ctx = ctx
 		end
+
+		def call(arg)
+			newctx = Context.new ctx
+			newctx[@argname] = arg
+			@ast.evaluate newctx
+		end
 	end
 
 	# To be used as Dhallish::Value.val for ruby-defined functions
@@ -342,6 +287,8 @@ module Dhallish
 		def initialize(&block)
 			@block = block
 		end
+
+		def call(arg) @block.call arg end
 	end
 
 	class Context
@@ -384,6 +331,7 @@ module Dhallish
 	end
 	module_function :mergeRecordTypes
 
+	# TODO: .val weg!
 	def mergeRecordsRecursively(a, b)
 		restype = mergeRecordTypes(a.type, b.type)
 
@@ -412,6 +360,7 @@ module Dhallish
 	end
 	module_function :mergeRecordTypesPrefereRight
 
+	# TODO: .val weg!
 	def mergeRecordsPrefereRight(a, b)
 		mergedType = mergeRecordTypesPrefereRight(a.type, b.type)
 		mergedVals = a.val.clone
diff --git a/lib/utils.rb b/lib/utils.rb
index 4670dc3..bb05b08 100644
--- a/lib/utils.rb
+++ b/lib/utils.rb
@@ -18,3 +18,7 @@ def get_new_sym()
 	$dhallish_internal_global_counter += 1
 	("__newsym_#{$dhallish_internal_global_counter}").to_sym
 end
+
+def escape_str(str)
+	str.gsub("\n", "\\n").gsub("\"", "\\\"")
+end
diff --git a/examples.dhallish b/tests/examples.dhallish
similarity index 92%
rename from examples.dhallish
rename to tests/examples.dhallish
index 8a50c39..db3b9f1 100644
--- a/examples.dhallish
+++ b/tests/examples.dhallish
@@ -36,15 +36,16 @@ let min = \(x: Natural) -> \(y: Natural) -> if x < y then x else y
 let List/atIndex = \(a: Type) -> \(list: List a) -> \(idx: Natural) ->
 	List/head a (Natural/fold idx (List a) (\(l: List a) -> List/tail a l) list)
 
+let Pair = \(a: Type) -> \(b: Type) -> { x: a, y: b }
+
 let List/zip = \(a: Type) -> \(b: Type) -> \(la: List a) -> \(lb: List b) -> \(dummyA: a) -> \(dummyB: b) ->
 	let idxList = genList(min (List/length a la) (List/length b lb))
-	let recType = { first: a, second: b }
-	let zipper = \(idx: Natural) -> \(list: List recType) ->
+	let zipper = \(idx: Natural) -> \(list: List (Pair a b)) ->
 		let first  = Optional/getOrElse a (List/atIndex a la idx) dummyA
 		let second = Optional/getOrElse b (List/atIndex b lb idx) dummyB
-		in list # [{ first = first, second = second }]
+		in list # [{ x = first, y = second }]
 	in
-		List/fold Natural idxList (List recType) zipper ([]: List recType)
+		List/fold Natural idxList (List (Pair a b)) zipper ([]: List (Pair a b))
 
 in {
 	math = (
diff --git a/examples.expected.json b/tests/examples_expected.json
similarity index 67%
rename from examples.expected.json
rename to tests/examples_expected.json
index b6eaab0..c874d2a 100644
--- a/examples.expected.json
+++ b/tests/examples_expected.json
@@ -9,5 +9,5 @@
 		{ "a": 3, "b": 9 },
 		{ "a": 4, "b": 16 } ],
 	"fibonacci": [ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ],
-	"zip": [ { "first": 1, "second": "one" }, { "first": 2, "second": "two" }, { "first": 3, "second": "three" }, { "first": 4, "second": "four" } ]
+	"zip": [ { "x": 1, "y": "one" }, { "x": 2, "y": "two" }, { "x": 3, "y": "three" }, { "x": 4, "y": "four" } ]
 }
diff --git a/tests/test.rb b/tests/test.rb
index 6af6663..47f32b1 100755
--- a/tests/test.rb
+++ b/tests/test.rb
@@ -33,7 +33,8 @@ positive_tests = {
 	"simple function type annotation": [ "(Natural/show : Natural -> Text) 2", "2" ],
 	"identity with type parameter": ["let f = \\(t : Type) -> \\(x : t) -> x in (f : forall(t : Type) -> t -> t) Natural 2", 2],
 	"function with two type parameters": ["let f = \\(t : Type) -> \\(u : Type) -> \\(x : t) -> \\(y : u) -> x in (f : forall(t : Type) -> forall(u : Type) -> t -> u -> t) Natural Integer 1 -1  ", 1 ],
-	"fallback for file import": ["let x = ./foobarfoobar.dhall as Text ? 1 in x", 1]
+	"fallback for file import": ["let x = ./foobarfoobar.dhall as Text ? 1 in x", 1],
+	"more type annotation": [ "let f = \(a: Type) -> \(l: List a) -> \(x: a) -> x in (f: forall(b: Type) -> forall(l: List b) -> b -> b) Natural [1, 2, 3] 5", 5 ]
 }
 
 negative_tests = {
@@ -128,4 +129,3 @@ dhallfiles.each { |file|
 		end
 	end
 }
-
-- 
GitLab