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

Rethink 'with' naming pattern for constructors? #140

Open
ianmackenzie opened this issue Oct 14, 2020 · 6 comments
Open

Rethink 'with' naming pattern for constructors? #140

ianmackenzie opened this issue Oct 14, 2020 · 6 comments
Assignees

Comments

@ianmackenzie
Copy link
Owner

ianmackenzie commented Oct 14, 2020

Several elm-geometry constructors use a similar naming pattern using with as part of the name, e.g.:

Circle2d.withRadius : Quantity Float units -> Point2d units coordinates -> Circle2d units coordinates
Axis3d.withDirection : Direction3d coordinates -> Point3d units coordinates -> Axis3d units coordinates
Vector2d.withLength : Quantity Float units -> Direction2d coordinates -> Vector2d units coordinates

These are reasonably concise (e.g. compare Vector2d.withLength to Vector2d.fromLengthAndDirection) and work well with partial application; for example, you can create a bunch of circles with the same radius at different points using

List.map (Circle2d.withRadius (Length.centimeters 5)) listOfPoints

However, there are some downsides:

  • As pointed out by @MartinSStewart, the names can sound more like 'modifiers' than 'constructors', e.g. Vector2d.withLength sounds like it might take an existing Vector2d and adjust it to have the given length
  • The with keyword has specific meanings in other programming languages, such as referring to resource handling/cleanup in Python, which may be confusing

For the next major release of elm-geometry, it may be worth reconsidering this pattern and seeing if there's an alternative that is more clear.

@MartinSStewart
Copy link

MartinSStewart commented Oct 14, 2020

I think your suggestion in Slack to use Vector2d.fromLengthAndDirection seems like a good approach. You mentioned drawbacks being it's verbose and doesn't work well with piping but I think the verbosity is made up for due to it being more discoverable and the piping issue doesn't seem like an issue to me since creating a vector is not as often used in the middle of a pipe.

I think it would be ideal if I could write "ModuleName.from" and have my editor be able to list all the constructors for that type. Right now, sometimes it's from*, or with*, or something else and I need to browse all the functions in a module to make sure I haven't missed something.

@ianmackenzie
Copy link
Owner Author

Never mentioned piping, I mentioned partial application 🙂 Piping is one use of partial application, but I think it can also be useful when doing things like mapping over Lists (as in the example), Maybes, JSON Decoders etc.

Your point is well taken, though - looking through my code, I think most uses of Vector3d.withLength, Circle2d.withRadius etc. would arguably be clearer as Vector3d.fromLengthAndDirection, Circle2d.fromCenterPointAndRadius etc. I guess I could add the new names and see how they feel, then deprecate the old ones and eventually remove them in the next major release.

Ooh, and I do really like these somewhat-weird constructors, I use them quite a bit:

Vector2d.from : Point2d units coordinates -> Point2d units coordinates -> Vector2d units coordinates
Vector3d.from : Point3d units coordinates -> Point3d units coordinates -> Vector3d units coordinates
LineSegment2d.from : Point2d units coordinates -> Point2d units coordinates -> LineSegment2d units coordinates
LineSegment3d.from : Point3d units coordinates -> Point3d units coordinates -> LineSegment3d units coordinates
Arc2d.from : Point2d units coordinates -> Point2d units coordinates -> Angle -> Arc2d units coordinates

I guess those still count as "starting with from", though 😛

@ianmackenzie
Copy link
Owner Author

A couple questions/thoughts (partially just thinking to myself here, but happy to take suggestions):

  • Would very short constructors like Point2.xy be changed to Point2d.fromXY?
  • Similarly, would Point2d.meters change to Point2d.fromMeters? If so, it would conflict with the existing Point2d.fromMeters that takes an { x : Float, y : Float } record - but maybe that could be renamed to Point2d.fromRecordInMeters or similar, or simply be removed in favour of just using Point2d.unsafe in that situation.

@MartinSStewart
Copy link

MartinSStewart commented Oct 15, 2020

To me Point.fromXY is worth it for consistency (even if Point2d.xy is otherwise a good name).

Not sure what I think about the rest. Lots of tricky decisions!

I think the from constructors are fine. Most of them create types in a "canonical" way so it makes sense that they are just "from".

@ianmackenzie
Copy link
Owner Author

Could also just rename xy to fromCoordinates - even longer (I think I remember you saying you were happy to move away from that name a while ago!) but pretty consistent with xCoordinate, yCoordinate and coordinates.

@MartinSStewart
Copy link

MartinSStewart commented Oct 16, 2020

Yeah, for me xy is a function I use often enough that I find fromCoordinates to be excessively long. fromXY isn't too bad though.

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

No branches or pull requests

2 participants