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

Alternative to specify open/closed endpoints #39

Closed
andyferris opened this issue Sep 6, 2018 · 10 comments · Fixed by #177
Closed

Alternative to specify open/closed endpoints #39

andyferris opened this issue Sep 6, 2018 · 10 comments · Fixed by #177

Comments

@andyferris
Copy link

andyferris commented Sep 6, 2018

In AcceleratedArrays.jl I've been playing with "search intervals" for querying data, for example finding all the dates within a given range with the help of a sort-based acceleration index.

To accommodate open and closed intervals, I've been toying with the idea of syntax that looks like a..exclude(b), which would be closed on the lower bound and open on the upper bound. exclude(b) creates an Exclude object which slightly redefines isequal and isless. See this file, particularly near the bottom.

Basically, I wonder if we've got our bananas turned inside out and whether it would be simpler/more composible to make this a property of the elements (endpoints) of the interval rather than of the interval itself?

@dlfivefifty
Copy link
Member

Exclude doesn’t have any mathematical meaning that I can think of...

@andyferris
Copy link
Author

andyferris commented Sep 6, 2018

Right... I was thinking that you technically may need something like infinitesimally_larger_than(start) .. infinitesimally_smaller_than(stop). These define the previous (or next) value acording to the (isequal, isless) total ordering. (They don't have to be the same Julia type as start or stop).

I guess it was my feeling that such an interface might be simpler for users and simpler in implementation - it's just the composition of a small number of very simple concepts, rather than having a variety of different interval types and a larger set of interval-reflection functions, etc.

@dlfivefifty
Copy link
Member

The simplest implementation is always the one that’s already been done...

I can see some benefits of your suggestion, but implementation wise it would require having 3 templates variables: the left endpoint type, the right endpoint type, and the domain type. This might turn into a headache.

If it’s the user facing syntax that’s wanted, that’s easy to add:

..(a::Exclude, b::Exclude) = OpenInterval(a,b)

@dlfivefifty
Copy link
Member

PS I’d prefer a swift like syntax a ..< b but that requires parser changes.

@andyferris
Copy link
Author

andyferris commented Sep 6, 2018

I'm not currently convinced you need a domain type. You can simply trust isless and isequal to sort out their own promotions for in, etc.

If it’s the user facing syntax that’s wanted, that’s easy to add

Right, that's true!

PS I’d prefer a swift like syntax a ..< b but that requires parser changes.

Something like this would be pretty sweet. Alternatively, a unary op for "infinitesimally smaller than" could do a similar thing and be visually similar, I guess.

@dlfivefifty
Copy link
Member

In SingularIntegralEquations.jl I use the following 1⁻/1⁺ and (x)⁻/(x)⁺. This is possible using something like:

struct Exclude{S,T}
    x::T
end
Exclude{S}(x::T) where {S,T} = Exclude{S,T}(x)
const= Exclude{true}(true)
const= Exclude{false}(true)
*(a::Number, b::Exclude{S}) where S = Exclude{S}(a*b.x)

@afbarnard
Copy link

Here's a Haskell package that puts the open- / closed-ness into the bounds. It also has syntactical sugar for creating intervals similar to that suggested above: a <..< b, a <=..< b, a <..<= b, a <=..<= b. It could be a good reference for design / API questions like this.

@hyrodium
Copy link
Collaborator

How about using macro?

  • @open 1..2 (Interval{:open,:open}(1,2))
  • @leftopen 1..2 (Interval{:open,:closed}(1,2))
  • @rightopen 1..2 (Interval{:closed,:open}(1,2))

@aplavin
Copy link
Contributor

aplavin commented Dec 8, 2023

Existing syntax that could easily be used:

julia> a .. <(b)
julia> a .. (b)
julia> >(a) .. <(b)

Looks quite nice for the right endpoint, the left one could've been better...

@hyrodium
Copy link
Collaborator

hyrodium commented Dec 9, 2023

From @MasonProtter on slack:

julia> using IntervalSets

julia> macro range_str(s)
           for (reg, f)  [r"^\[.*\)$" => Interval{:closed, :open}, 
                           r"^\(.*\)$" => Interval{:open, :open},
                           r"^\(.*\]$" => Interval{:open, :closed},
                           r"^\[.*\]$" => Interval{:closed, :closed}]
               m = match(reg, s)
               if !isnothing(m)
                   args = Meta.parse(s[nextind(s, 1):prevind(s, lastindex(s))])
                   return :($f($(esc(args))...))
               end
           end
           error("unrecognized expresson $s")
       end;

julia> let a = 2
           range"[1,a)"
       end
1 .. 2 (closed-open)

https://julialang.slack.com/archives/C680MM7D4/p1701897935053029?thread_ts=1701897085.625229&cid=C680MM7D4

I think range"[1,2]" should be interval"[1,2]" (or iv"[1,2]" for short) because the returned value is not an AbstractRange, but using string macro is a great idea.

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

Successfully merging a pull request may close this issue.

5 participants