elm-geometry icon indicating copy to clipboard operation
elm-geometry copied to clipboard

Rethink 'with' naming pattern for constructors?

Open ianmackenzie opened this issue 3 years ago • 6 comments

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.

ianmackenzie avatar Oct 14 '20 15:10 ianmackenzie

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.

MartinSStewart avatar Oct 14 '20 20:10 MartinSStewart

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 avatar Oct 15 '20 01:10 ianmackenzie

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.

ianmackenzie avatar Oct 15 '20 01:10 ianmackenzie

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".

MartinSStewart avatar Oct 15 '20 17:10 MartinSStewart

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.

ianmackenzie avatar Oct 15 '20 21:10 ianmackenzie

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.

MartinSStewart avatar Oct 16 '20 16:10 MartinSStewart