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

Issues modeling GeoJSON spec with rescript@11 types #6277

Closed
cristianoc opened this issue May 30, 2023 Discussed in #6273 · 4 comments · Fixed by #6721
Closed

Issues modeling GeoJSON spec with rescript@11 types #6277

cristianoc opened this issue May 30, 2023 Discussed in #6273 · 4 comments · Fixed by #6721
Milestone

Comments

@cristianoc
Copy link
Collaborator

Discussed in #6273

Originally posted by glennsl May 28, 2023
I've been trying to model GeoJSON using rescript@11 types, just to see if I can, and am stumbling on just a few issues that I think should be feasible (though not easy) to fix since all the necessary features do exist in various forms. So I figured I'd document this as a prominent use case.

See the full TypeScript type definitions for reference.

The big problem is that a GeoJSON document can be either a single geometry object (of which there are several kinds), a "feature" which contains geometry, or a feature collection. These all use a type tag, but because geometry objects are included in several different types, modeling this requires structural typing. And unfortunately @tag only works on nominal variants, not structural (polymorphic) ones. This is also the case with inline records, which is necessary for @tag to be useful, so that's another hurdle.

Essentially, I'd like to define the types like this:

@tag("type")
type rec geometry = [
  | #Point({coordinates: pos})
  | #MultiPoint({coordinates: array<pos>})
  | #LineString({coordinates: array<pos>})
  | #MultiLineString({coordinates: array<array<pos>>})
  | #Polygon({coordinates: array<array<pos>>})
  | #MultiPolygon({coordinates: array<array<array<pos>>>})
  | #GeometryCollection({coordinates: array<geometry>})
]

@tag("type")
type feature = [
  | #Feature({geometry: geometry})
]

@tag("type")
type t = [
  | geometry
  | feature
  | #FeatureCollection({features: array<feature>})
]

Another minor problem is that positions and bounding boxes are arrays of different arities. These could be modeled with an unboxed tagged variant containing tuples of different arities, and possibly a constructor an array to cover the remaining arities. E.g.:

@unboxed
type pos = 
  | LatLng((float, float))
  | LatLngAlt((float, float, float))
  | M(array<float>)

Lots of edge cases to consider though. A further improvement could be to (optionally) model inline records in unboxed variants as arrays, so that it could be:

@unboxed
type pos = 
  | LatLng({lat: float, lng: float})
  | LatLngAlt({lat: float, lng: float, alt: float})
  | M(array<float>)
```</div>
@tsnobip
Copy link
Contributor

tsnobip commented Jan 4, 2024

This is a quite interesting use-case.

I don't see how we could have different shapes for structural variants.

But couldn't we use nominal variants here since we can then coerce them to another type?

@tag("type")
type rec geometry = 
  | Point({coordinates: pos})
  | MultiPoint({coordinates: array<pos>})
  | LineString({coordinates: array<pos>})
  | MultiLineString({coordinates: array<array<pos>>})
  | Polygon({coordinates: array<array<pos>>})
  | MultiPolygon({coordinates: array<array<array<pos>>>})
  | GeometryCollection({coordinates: array<geometry>})


let point = Point({coordinates: {lat: 1.0, long: 2.0}})

@tag("type")
type feature = 
  | Feature({geometry: geometry})


@tag("type")
type t = 
  | ...geometry
  | ...feature
  | FeatureCollection({features: array<feature>})


let geometry = point :> t

@glennsl
Copy link
Contributor

glennsl commented Jan 4, 2024

Yeah, that works for defining it (since 11.0.0-beta.4), but unfortunately you can't then narrow the type again (going from t to feature or geometry), which means you can't make generalized functions that operate on those specifically. See the comment here: #6273 (reply in thread)

@zth
Copy link
Collaborator

zth commented Jan 5, 2024

That capability would indeed be great to have in the toolbox.

@tsnobip
Copy link
Contributor

tsnobip commented Jan 5, 2024

Yeah, that works for defining it (since 11.0.0-beta.4), but unfortunately you can't then narrow the type again (going from t to feature or geometry), which means you can't make generalized functions that operate on those specifically. See the comment here: #6273 (reply in thread)

Oh yeah I've already been bitten by this quite many times, it'd indeed be great to be able to pattern-match on subtype of variants, as can be done on polymorphic variants.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants