@@ -36,6 +36,7 @@
3636#include <string>
3737#include <array>
3838#include <deque>
39+#include <unordered_map>
3940#include <utility>
4041#include <stdexcept>
4142#include <iso646.h> // mostly for MS Visual Studio compiler
@@ -61,14 +62,14 @@ namespace Private {
6162 using Symbol = const SymbolGeneratorAnchor *;
6263}
6364
64−#define define_symbol(S) namespace PureScript { \
65+#define DEFINE_SYMBOL(S) namespace PureScript { \
6566 namespace Private { \
6667 namespace Symbols { \
6768 struct S_ ## S {}; \
6869 } \
6970 } \
7071 }
71−#define symbol(S) (&Private::SymbolGenerator<::PureScript::Private::Symbols::S_ ## S>::anchor)
72+#define SYM(S) (&Private::SymbolGenerator<::PureScript::Private::Symbols::S_ ## S>::anchor)
7273
7374constexpr bool undefined = false;
7475
@@ -93,6 +94,7 @@ class any {
9394 Map,
9495 Data,
9596 Array,
97+ Record,
9698 Closure,
9799 EffClosure,
98100 Pointer
@@ -106,15 +108,17 @@ class any {
106108 };
107109 static constexpr as_thunk unthunk = as_thunk{};
108110
109− using map_pair = std::pair<const Private::Symbol, const any>;
111+ using dict_pair = std::pair<const Private::Symbol, const any>;
110112
111113 template <size_t N>
112− using map = std::array<const map_pair, N>;
114+ using dict = std::array<const dict_pair, N>;
113115
114116 template <size_t N>
115117 using data = std::array<const any, N>;
116118
117− using array = std::deque<any WITH_ALLOCATOR(any)>;
119+ using array = std::deque<any WITH_ALLOCATOR(any)>;
120+ using record = std::unordered_map<std::string, any WITH_ALLOCATOR(any)>;
121+
118122 using fn = auto (*)(const any&) -> any;
119123 using eff_fn = auto (*)() -> any;
120124 using thunk = auto (*)(const as_thunk) -> const any&;
@@ -164,6 +168,7 @@ class any {
164168 mutable void * v;
165169 mutable managed<string> s;
166170 mutable managed<array> a;
171+ mutable managed<record> r;
167172 mutable managed<Closure> l;
168173 mutable managed<EffClosure> k;
169174 mutable managed<void> p;
@@ -191,7 +196,7 @@ class any {
191196 any(managed<string>&& val) noexcept : tag(Tag::String), s(std::move(val)) {}
192197
193198 template <size_t N>
194− any(map<N>&& val) noexcept : tag(Tag::Map), p(make_managed<map<N>>(std::move(val))) {}
199+ any(dict<N>&& val) noexcept : tag(Tag::Map), p(make_managed<dict<N>>(std::move(val))) {}
195200
196201 template <size_t N>
197202 any(data<N>&& val) noexcept : tag(Tag::Data), p(make_managed<data<N>>(std::move(val))) {}
@@ -199,6 +204,9 @@ class any {
199204 any(const array& val) : tag(Tag::Array), a(make_managed<array>(val)) {}
200205 any(array&& val) noexcept : tag(Tag::Array), a(make_managed<array>(std::move(val))) {}
201206
207+ any(const record& val) : tag(Tag::Record), r(make_managed<record>(val)) {}
208+ any(record&& val) noexcept : tag(Tag::Record), r(make_managed<record>(std::move(val))) {}
209+
202210 template <typename T>
203211 any(const T& val, typename std::enable_if<std::is_convertible<T,fn>::value>::type* = 0) noexcept
204212 : tag(Tag::Function), f(val) {}
@@ -269,6 +277,7 @@ class any {
269277
270278 case Tag::String: new (&s) managed<string>(move_if_rvalue<T>(other.s)); break;
271279 case Tag::Array: new (&a) managed<array>(move_if_rvalue<T>(other.a)); break;
280+ case Tag::Record: new (&r) managed<record>(move_if_rvalue<T>(other.r)); break;
272281 case Tag::Closure: new (&l) managed<Closure>(move_if_rvalue<T>(other.l)); break;
273282 case Tag::EffClosure: new (&k) managed<EffClosure>(move_if_rvalue<T>(other.k)); break;
274283 case Tag::Map:
@@ -283,6 +292,7 @@ class any {
283292 switch (tag) {
284293 case Tag::String: s.~managed<string>(); break;
285294 case Tag::Array: a.~managed<array>(); break;
295+ case Tag::Record: r.~managed<record>(); break;
286296 case Tag::Closure: l.~managed<Closure>(); break;
287297 case Tag::EffClosure: k.~managed<EffClosure>(); break;
288298 case Tag::Map:
@@ -337,9 +347,10 @@ class any {
337347 operator const string&() const;
338348 operator const char *() const;
339349 operator const array&() const;
350+ operator const record&() const;
340351
341352 auto operator[](const size_t) const -> const any&;
342−
353+ auto at(const std::string&) const -> const any&;
343354 auto size() const -> size_t;
344355 auto empty() const -> bool;
345356 auto contains(const Private::Symbol) const -> bool;
@@ -419,7 +430,7 @@ namespace Private {
419430 struct TagHelper {};
420431
421432 template <size_t N>
422− struct TagHelper<any::map<N>> {
433+ struct TagHelper<any::dict<N>> {
423434 static constexpr any::Tag tag = any::Tag::Map;
424435 };
425436
@@ -450,14 +461,16 @@ inline auto cast(const any& a) ->
450461template <typename T>
451462inline auto cast(const any& a) ->
452463 typename std::enable_if<std::is_same<T, string>::value ||
453− std::is_same<T, any::array>::value, const T&>::type {
464+ std::is_same<T, any::array>::value ||
465+ std::is_same<T, any::record>::value, const T&>::type {
454466 return a;
455467}
456468
457469template <typename T,
458470 typename = typename std::enable_if<std::is_class<T>::value &&
459471 !std::is_same<T,string>::value &&
460− !std::is_same<T,any::array>::value>::type>
472+ !std::is_same<T,any::array>::value &&
473+ !std::is_same<T,any::record>::value>::type>
461474inline auto cast(const any& a) -> T& {
462475 return *static_cast<T*>(a.extractPointer(IF_DEBUG(Private::TagHelper<T>::tag)));
463476}
@@ -476,7 +489,7 @@ inline auto cast(const any& a) ->
476489 return a.rawPointer();
477490}
478491
479−namespace map {
492+namespace dict {
480493 template <size_t N, typename T>
481494 inline auto get(const T& a) ->
482495 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
@@ -485,29 +498,29 @@ namespace map {
485498
486499 template <size_t N>
487500 inline auto get(const any& a) -> const any& {
488− return cast<any::map<N+1>>(a)[N].second;
501+ return cast<any::dict<N+1>>(a)[N].second;
489502 }
490503
491504 template <size_t N>
492− inline auto get(const Private::Symbol key, const any::map<N>& a) -> const any& {
493− static_assert(N > 0, "map size must be greater than zero");
505+ inline auto get(const Private::Symbol key, const any::dict<N>& a) -> const any& {
506+ static_assert(N > 0, "dict size must be greater than zero");
494507 typename std::remove_reference<decltype(a)>::type::size_type i = 0;
495508 do {
496509 if (a[i].first == key) {
497510 return a[i].second;
498511 }
499512 } while (a[++i].first != nullptr);
500− assert(false && "map key not found");
513+ assert(false && "dict key not found");
501514 static const any invalid_key(nullptr);
502515 return invalid_key;
503516 }
504517
505518 inline auto get(const Private::Symbol key, const any& a) -> const any& {
506− return get(key, cast<any::map<unknown_size>>(a));
519+ return get(key, cast<any::dict<unknown_size>>(a));
507520 }
508521
509− #define P(N) any::map_pair&& arg ## N
510− #define T(N) any::map<N+1>
522+ #define P(N) any::dict_pair&& arg ## N
523+ #define T(N) any::dict<N+1>
511524 #define E(N) std::forward<decltype(arg ## N)>(arg ## N)
512525 #define M(...) {{ __VA_ARGS__, { nullptr, nullptr } }}
513526
@@ -542,8 +555,6 @@ namespace map {
542555 #undef M
543556}
544557
545−namespace record = map;
546−
547558namespace data {
548559 template <size_t N, typename T>
549560 inline auto get(const T& a) ->