ElementEdge.diff uses Position.diff for error messages
ElementEdge.diff() uses Position.diff() when comparing vs number
ElementEdge.toString()
ElementEdge.diff turns numbers into position before checking them
Descriptors and values have isScalar() method
ElementEdge.diff() uses isScalar()
ElementEdge.describeMatch()
isScalar() obsoleted by describeMatch()
Position.is() (for polymorphism with ElementEdge)
Position no longer compares to primitive numbers
ElementEdge magic strings are in constants
INTEGRATE: Value objects and polymorphism simplify diff logic

* dev:
  ElementEdge magic strings are in constants
  Position no longer compares to primitive numbers
  Position.is() (for polymorphism with ElementEdge)
  isScalar() obsoleted by describeMatch()
  ElementEdge.describeMatch()
  ElementEdge.diff() uses isScalar()
  Descriptors and values have isScalar() method
  ElementEdge.diff turns numbers into position before checking them
  ElementEdge.toString()
  ElementEdge.diff() uses Position.diff() when comparing vs number
  ElementEdge.diff uses Position.diff for error messages
  ElementEdge uses Position.diff()
  Position.equals() uses .diff()
  Position.diff() is comparable to a number
  Position.diff() fails fast when comparing opposite dimensions
  Position.diff(): human readable difference in positions
  XPosition and YPosition merged into single Position class
  ElementEdge uses XPosition and YPosition value objects
  YPosition.equals() accepts a number
  XPosition.equals() accepts a number
  assert.equal provides better error when types are different
  XPosition class
  YPosition class
  Rename: constraints/ --> descriptors/
  ElementEdge.is(): resolves a constraint to a value
  ElementEdge.description()