spire icon indicating copy to clipboard operation
spire copied to clipboard

Provide way to print an Interval[T] without requiring an Order[T] instance

Open rklaehn opened this issue 10 years ago • 3 comments

In my data structures in intervalset, I do not store intervals, but only boundary positions. I also do not store the typeclasses in the data structure, but require them on each operation.

I would like to provide a (best-effort) toString method, but currently, there is no way to create an Interval[T] for printing without having an Order[T] (which I don't have.

In the long term I think it might be better to not store the Order instance with an interval, but that's another issue.

rklaehn avatar Dec 22 '15 09:12 rklaehn

Hi @rklaehn,

I don't see an easy way out, as you cannot even create an Interval[A] without an Order[A].

I'm for including Order in the interval itself, because the consistency of the Interval object depends on the particular order instance chosen.

And Order-like typeclasses are the typical example of typeclasses that have several possible implementations for the same type (more so on the PartialOrder side, actually).

denisrosset avatar Dec 22 '15 15:12 denisrosset

I "solved" my problem with the horrible hack of implementing a dummy order in the toString method, so I can create an Interval instance and then immediately call toString on it. I only provide toString for convenience anyway. I would prefer to just throw an UnsupportedOperationException on equals, hashcode and toString. But that would make the library a bit too painful to use in IDEs etc.

val dummyOrder: Order[K] = new Order[K] { def compare(x: K, y: K) = 0 }

Regarding including the oder on the interval itself: That won't protect you from horrible bugs if you combine two intervals with different order instances. E.g.

scala> val a = Interval(1, 2)
a: spire.math.Interval[Int] = [1, 2]

scala> val b = Interval(3, 2)(Order[Int].reverse)
b: spire.math.Interval[Int] = [3, 2]

scala> a union b
res4: spire.math.Interval[Int] = [1, 2] // huh?

Now you could argue that interval operations should throw an exception when they are called with two different order instances. But that won't work, because for derived typeclass instances it is not possible to define equality.

scala> val a = Order[(Int, Int)]
a: spire.algebra.Order[(Int, Int)] = spire.std.OrderProductInstances$$anon$175@53aa16bb

scala> val b = Order[(Int, Int)]
b: spire.algebra.Order[(Int, Int)] = spire.std.OrderProductInstances$$anon$175@52ce24d9

scala> a == b
res6: Boolean = false

So in my data structures I just assume that you have to provide consistent Order instances, or things will blow up in your face. I don't think there is anything you can do about this.

rklaehn avatar Dec 22 '15 16:12 rklaehn

Ok, understand your point. Still, Order is used during construction of the Interval instance, to decide, for example, whether the request closed interval is empty, just a point or a segment.

One way out: remove the Order instance in Interval (would save a bit of heap), and add non-protected unsafe construction methods in the companion object.

denisrosset avatar Mar 02 '16 03:03 denisrosset