generics.js 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. /**
  2. * TypeTemplate: Something just like generics but not generics
  3. *
  4. * A type template is a function-like type object, which can be inflated by
  5. * a fixed number of arguments (types or primitives) and returns a new type.
  6. * The arguments and returned type will be cached. If the same arguments
  7. * are provided in the next inflation, the cached type will be returned.
  8. * For any object x and type template TT, x ∈ TT if and only if there exists
  9. * a cached type T of TT that satisfies x ∈ T.
  10. */
  11. class TypeTemplate {
  12. constructor (inflater) {
  13. assert(is(inflater, Types.Function))
  14. let self = {
  15. inflater,
  16. cache: new VectorMapCache(),
  17. inflated: {
  18. classes: new Set(),
  19. interfaces: new Set(),
  20. schemas: new Set(),
  21. others: new Set(),
  22. all: new Set()
  23. }
  24. }
  25. this.inflate = this.inflate.bind(self)
  26. inject_desc(this.inflate, 'inflate_template')
  27. this.has_inflated = this.has_inflated.bind(self)
  28. this[Checker] = this.check.bind(self)
  29. Object.freeze(this)
  30. }
  31. check (x) {
  32. assert(!(this instanceof TypeTemplate))
  33. let { classes, interfaces, schemas, others } = this.inflated
  34. if (is(x, Types.Instance)) {
  35. if (exists(x.class_.super_classes, C => classes.has(C))) {
  36. return true
  37. }
  38. if (exists(x.class_.super_interfaces, I => interfaces.has(I))) {
  39. return true
  40. }
  41. } else if (is(x, Types.Struct)) {
  42. if (schemas.has(x.schema)) {
  43. return true
  44. }
  45. }
  46. return exists(others, T => is(x, T))
  47. }
  48. inflate (...args) {
  49. assert(is(args, Types.List))
  50. let cached = this.cache.find(args)
  51. if (cached !== NotFound) {
  52. return cached
  53. }
  54. foreach(args, (arg, i) => {
  55. let valid = is(arg, Type) || is(arg, Types.Primitive)
  56. ensure(valid, 'arg_invalid_inflate', `#${i+1}`)
  57. })
  58. let type = call(this.inflater, args)
  59. ensure(is(type, Type), 'retval_invalid_inflate')
  60. this.cache.set(args, type)
  61. if (is(type, Types.Class)) {
  62. this.inflated.classes.add(type)
  63. } else if (is(type, Types.Interface)) {
  64. this.inflated.interfaces.add(type)
  65. } else if (is(type, Types.Schema)) {
  66. this.inflated.schemas.add(type)
  67. } else {
  68. this.inflated.others.add(type)
  69. }
  70. this.inflated.all.add(type)
  71. return type
  72. }
  73. has_inflated (type) {
  74. assert(is(type, Types.Type))
  75. return this.inflated.all.has(type)
  76. }
  77. get [Symbol.toStringTag]() {
  78. return 'TypeTemplate'
  79. }
  80. }
  81. Types.TypeTemplate = $(x => x instanceof TypeTemplate)
  82. /* shorthand */
  83. let template = ((p,r) => new TypeTemplate(fun(p,r)))