Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"strict" record types #128

Open
davidchambers opened this issue Mar 18, 2017 · 4 comments
Open

"strict" record types #128

davidchambers opened this issue Mar 18, 2017 · 4 comments
Assignees

Comments

@davidchambers
Copy link
Member

Record types permit additional fields. For example:

const Point = $.RecordType({x: $.Number, y: $.Number});

$.test([], Point, {x: 0, y: 0});
// => true

$.test([], Point, {x: 0, y: 0, radius: 5});
// => true

One may wish to define a "strict" record type instead. One may do so using $.NullaryType:

const StrictPoint = $.NullaryType(
  'my-package/StrictPoint',
  '',
  x => $.test([], Point, x) && Object.keys(x).length === Point.keys.length
);

$.test([], StrictPoint, {x: 0, y: 0});
// => true

$.test([], StrictPoint, {x: 0, y: 0, radius: 5});
// => false

We could provide $.StrictRecordType, or have $.RecordType take an additional argument to specify whether additional fields are permitted. I'm not sure we should do so, though, given the solution above.

This issue serves both to document the existing solution, and to provide a place to discuss the pros and cons of supporting "strict" record types more directly.

@Avaq
Copy link
Member

Avaq commented Mar 20, 2017

Note that the existing solution is inferior to the proposed solution, specifically with regards to validate:

> const Point = $.RecordType({x: $.Number, y: $.Number});
> Point.validate({x: 1, y: '2'})
Left({value: "2", propPath: [ "y" ]})

> //However:
> const StrictPoint = $.NullaryType('StrictPoint', '',
>   x => $.test([], Point, x) && Object.keys(x).length === Point.keys.length
> );
> StrictPoint.validate({x: 1, y: '2'})
Left({value: { x: 1, y: "2" }, propPath: []})

So our StrictPoint loses track of where exactly the faulty data lives.

However, I don't think providing a StrictRecordType would be the appropriate solution, as all Types which includes a $.test of another type in their predicate (and there are quite a few) share the same problem.

I think the only way to solve this, is by providing a formal type refinement function similar to the refinement-combinator from TComb. It could look somewhat like this, and it would be trivial to create a StrictRecord helper to keep dry:

const Point = $.RecordType({x: $.Number, y: $.Number});

const StrictPoint = $.NullaryTypeRefinement(
  Point,
  'StrictPoint',
  x => Object.keys(x).length === Point.keys.length
);

The validate function would simply chain its own validation logic over the result of calling UnrefinedType.validate.

@davidchambers
Copy link
Member Author

Rather than define $.NullaryTypeRefinement I suggest we add a parameter of type Array Type to $.NullaryType:

 //    Integer :: Type
 const Integer = $.NullaryType(
   'my-package/Integer',
   'http://example.com/my-package#Integer',
-  x => typeof x === 'number' &&
+  [$.Number],
+  x => Math.floor(x) === x &&
        x >= Number.MIN_SAFE_INTEGER &&
        x <= Number.MAX_SAFE_INTEGER
 );

 //    NonZeroInteger :: Type
 const NonZeroInteger = $.NullaryType(
   'my-package/NonZeroInteger',
   'http://example.com/my-package#NonZeroInteger',
-  x => $.test([], Integer, x) && x !== 0
+  [Integer],
+  x => x !== 0
 );

If you're interested in working on this change, @Avaq, I will leave it to you. Otherwise I will get to it myself at some point.

@Avaq
Copy link
Member

Avaq commented May 30, 2017

Does it make sense to add this parameter only to NullaryType? What about types with other arities?

@davidchambers
Copy link
Member Author

Does it make sense to add this parameter only to NullaryType? What about types with other arities?

I was imagining making the same change to $.UnaryType and $.BinaryType as well. I don't think it makes sense for $.EnumType, but it would be very nice for $.RecordType:

//    Point :: Type
const Point = $.RecordType([], {x: $.FiniteNumber, y: $.FiniteNumber});

//    Color :: Type
const Color = $.NullaryType(...);

//    ColorPoint :: Type
const ColorPoint = $.RecordType([Point], {color: Color});

$.test([], Point, {x: 0, y: 0});                        // => true
$.test([], Point, {x: 0, y: 0, color: '#000'});         // => true
$.test([], ColorPoint, {x: 0, y: 0});                   // => false
$.test([], ColorPoint, {x: 0, y: 0, color: '#000'});    // => true

@Avaq Avaq self-assigned this Jun 25, 2017
@Avaq Avaq mentioned this issue Jun 26, 2017
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants