Implementing a zero width type for IntervalSets

This is about using the foundations of IntervalSets.jl for my own purposes.

I’d like to be able to dispatch on sets that have zero width (i.e. sets whose end points are identical) differently than on sets with a width larger than zero. So I want to define a new subtype of AbstractInterval (e.g. CollapsedInterval or some such), and all the functions necessary for all the machinery of IntervalSets to work as expected. Basically have the same behavior as if I tested IntervalSets on, say, 1..1.

For this to work, what is the minimal set of functions I should define for my subtype? I’m asking because in their README.md#L12 they state that this package is intended to be built on, but I couldn’t find the minimal set of functions I’d need to define.

Thanks!

1 Like

The comments here might help:

First: do you know at compile time whether the endpoints are open or closed? If so, subtype TypedEndpointsInterval{:closed,:closed} and then I believe you only need to override endpoints. Otherwise, also override closedendpoints.

PRs to improve the docs would be appreciated.

Very cool, thank you, but, why are the arguments of duration limited to integers and dates…? Wouldn’t it be better to define it along similar lines to the implementation of width:

function duration(A::AbstractInterval)
    _width = rightendpoint(A) - leftendpoint(A)
    max(zero(_width), _width + oneunit(_width))   # this works when T is a Date
end

This way it should work with any typed interval.

I didn’t write that function or use it so wouldn’t know. Maybe make a PR and see if anyone pipes up?

will do! will wait for @Tamas_Papp’s response (can see him typing)

I actually deleted what I was typing, wondering if it made sense. The gist was that I would just define a type wrapping a <:Real, and for that endpoints, and closedendpoints, and then whatever else is needed for your functionality, then I guess you should be fine.

Another solution would be a trait-like

iszerowidth(interval) = Val{leftendpoint(interval) = rightendpoint(interval)}()

which is dynamic per se, but propagating this would make the callees type stable.

Did the PR.

This was what I started with (and might return to), cause really, I don’t need that much. But I love the idea of these light meta-packages, such as IntervalSets, that can be used everywhere, so I figured I might try to make use of their existing API. But if implementing a subtype is more tedious than implementing a wrapper with needed functionality then it defeats the purpose.

(* changed the = to ==) I need to read more about traits. How exactly would this work…?

broadly

do_stuff(interval) = _do_lots_of_stuff(iszerowidth(interval), interval)

_do_lots_of_stuff(::Val{true}, interval) = ...

The first layer would use dynamic dispatch, eg as an if. Come to think of it, even a branch should not be that costly within a function, so this may not be worth it.

2 Likes