Skip to content

Commit b38eb6d

Browse files
S. SahmS. Sahm
authored andcommitted
reimplemented everything using Union types,
Now it looks like a natural next step is to simplify the type world and take Some/Right/Success together into Identity.
1 parent 5952345 commit b38eb6d

10 files changed

Lines changed: 227 additions & 252 deletions

File tree

src/ContextManager.jl

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,9 @@ Base.map(f, c::ContextManager) = @ContextManager cont -> begin
4747
c(x -> cont(f(x)))
4848
end
4949

50-
# we do flatten via a mere TypeWrapper to be able to do flattening at runtime if type inference is not good enough
51-
Iterators.flatten(c::ContextManager) = map(FlattenMe, c)
52-
function Iterators.flatten(contextmanager::ContextManager)
53-
@ContextManager cont -> begin
54-
# execute both nested ContextManagers in the nested manner
55-
contextmanager() do inner_contextmanager
56-
convert(ContextManager, inner_contextmanager)(cont)
57-
end
50+
Iterators.flatten(contextmanager::ContextManager) = @ContextManager cont -> begin
51+
# execute both nested ContextManagers in the nested manner
52+
contextmanager() do inner_contextmanager
53+
convert(ContextManager, inner_contextmanager)(cont)
5854
end
5955
end

src/DataTypesBasic.jl

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@ functions, we go with Approach 2. That applies to Option, Try and Either.
2929
"""
3030
module DataTypesBasic
3131

32-
export @overwrite_Some,
33-
Const,
32+
export Const,
3433
Identity,
35-
Option, None, Some, issomething, iftrue, iffalse, getOption, # isnothing comes from Base
36-
Either, Left, Right, either, isleft, isright, getleft, getright, getleftOption, getrightOption, getEither,
34+
Option, issomething, iftrue, iffalse, getOption, # isnothing, Nothing, Some comes from Base
35+
Either, Left, Right, either, isleft, isright, getleft, getright, getleftOption, getrightOption, getEither, flip_left_right,
3736
Try, Success, Failure, @Try, @TryCatch, issuccess, isfailure, MultipleExceptions,
3837
ContextManager, @ContextManager
3938

@@ -47,18 +46,5 @@ include("Either.jl")
4746
include("ContextManager.jl")
4847
include("convert.jl")
4948

50-
macro overwrite_Base()
51-
esc(quote
52-
const Some = DataTypesBasic.Some
53-
nothing
54-
end)
55-
end
56-
57-
macro overwrite_Some()
58-
esc(quote
59-
const Some = DataTypesBasic.Some
60-
nothing
61-
end)
62-
end
6349

6450
end # module

src/Either.jl

Lines changed: 75 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,106 @@
1-
abstract type Either{L, R} end
2-
3-
# variable naming taken from Base.Some
4-
struct Left{L, R} <: Either{L, R}
1+
struct Left{L}
52
value::L
63
end
7-
Left{L}(x::L) where {L} = Left{L, Any}(x)
8-
Left(x::L) where {L} = Left{L, Any}(x)
9-
# TODO the typecast conflict with nested applications
10-
# Left{L, R}(x::Left{L}) where {L, R} = Left{L, R}(x.value)
11-
# Left{R}(x::Left{L}) where {L, R} = Left{L, R}(x.value)
124

13-
struct Right{L, R} <: Either{L, R}
5+
struct Right{R}
146
value::R
157
end
16-
Right{L}(x::R) where {L, R} = Right{L, R}(x)
17-
Right(x::R) where {R} = Right{Any, R}(x)
18-
# TODO the typecast conflict with nested applications
19-
# Right{L}(x::Right{<:Any, R}) where {L, R} = Right{L, R}(x.value)
20-
21-
# TODO the typecast conflict with nested applications
22-
# Either{L, R}(x::Left{L}) where {L, R} = Left{L, R}(x.value)
23-
# Either{L, R}(x::Right{<:Any, R}) where {L, R} = Right{L, R}(x.value)
24-
Either{L, R}(x::L) where {L, R} = Left{L, R}(x)
25-
Either{L, R}(x::R) where {L, R} = Right{L, R}(x)
26-
Either{L, R}(x::L) where {L, R} = Left{L, R}(x)
27-
Either{L, R}(x::R) where {L, R} = Right{L, R}(x)
288

29-
Either{L}(x::R) where {L, R} = Right{L, R}(x)
30-
Either{L}(x::L) where {L} = Left{L, Any}(x)
9+
const Either{L, R} = Union{Left{L}, Right{R}}
10+
Either{L, R}(x::L) where {L, R} = Left(x)
11+
Either{L, R}(x::R) where {L, R} = Right(x)
12+
Either{L}(x::R) where {L, R} = Right(x)
13+
Either{L}(x::L) where {L} = Left(x)
14+
15+
16+
# conversions/ promotions
17+
18+
# typejoin Left & Left
19+
Base.typejoin(::Type{Left{L}}, ::Type{Left{L}}) where L = Left{L}
20+
Base.typejoin(::Type{<:Left}, ::Type{<:Left}) = Left
21+
# typejoin Right & Right
22+
Base.typejoin(::Type{Right{R}}, ::Type{Right{R}}) where R = Right{R}
23+
Base.typejoin(::Type{<:Right}, ::Type{<:Right}) = Right
24+
# typejoin Left & Right
25+
Base.typejoin(::Type{Left{L}}, ::Type{Right{R}}) where {L, R} = Either{L, R}
26+
Base.typejoin(::Type{Right{R}}, ::Type{Left{L}}) where {L, R} = Either{L, R}
27+
# typejoin Left & Either
28+
Base.typejoin(::Type{Left{L}}, ::Type{<:Either{L, R}}) where {L, R} = Either{L, R}
29+
Base.typejoin(::Type{<:Either{L, R}}, ::Type{Left{L}}) where {L, R} = Either{L, R}
30+
Base.typejoin(::Type{<:Left}, ::Type{<:Either{<:Any, R}}) where {R} = Either{<:Any, R}
31+
Base.typejoin(::Type{<:Either{<:Any, R}}, ::Type{<:Left}) where {R} = Either{<:Any, R}
32+
# typejoin Right & Either
33+
Base.typejoin(::Type{Right{R}}, ::Type{<:Either{L, R}}) where {L, R} = Either{L, R}
34+
Base.typejoin(::Type{<:Either{L, R}}, ::Type{Right{R}}) where {L, R} = Either{L, R}
35+
Base.typejoin(::Type{<:Right}, ::Type{<:Either{L}}) where {L} = Either{L}
36+
Base.typejoin(::Type{<:Either{L}}, ::Type{<:Right}) where {L} = Either{L}
37+
# typejoin Either & Either
38+
Base.typejoin(::Type{<:Either{L, R}}, ::Type{<:Either{L, R}}) where {L, R} = Either{L, R}
39+
Base.typejoin(::Type{<:Either{L}}, ::Type{<:Either{L}}) where {L} = Either{L}
40+
Base.typejoin(::Type{<:Either{<:Any, R}}, ::Type{<:Either{<:Any, R}}) where {R} = Either{<:Any, R}
41+
Base.typejoin(::Type{<:Either}, ::Type{<:Either}) = Either
42+
43+
# Left/Right are covariate
44+
Base.convert(::Type{Right{T}}, x::Right{S}) where {S, T} = Right(Base.convert(T, x.value))
45+
Base.convert(::Type{Left{T}}, x::Left{S}) where {S, T} = Left(Base.convert(T, x.value))
46+
Base.convert(::Type{Either{L, R2}}, x::Right{R}) where {L, R, R2} = Right(Base.convert(R2, x.value))
47+
Base.convert(::Type{Either{L2, R}}, x::Left{L}) where {L, L2, R} = Left(Base.convert(L2, x.value))
48+
promote_rule(::Type{Left{T}}, ::Type{Left{S}}) where {T, S<:T} = Left{T}
49+
promote_rule(::Type{Right{T}}, ::Type{Right{S}}) where {T, S<:T} = Right{T}
3150

3251

3352
# == controversy https://github.com/JuliaLang/julia/issues/4648
34-
Base.:(==)(a::Either, b::Either) = either_compare(a, b)
35-
either_compare(a::Right, b::Right) = a.value == b.value
36-
either_compare(a::Left, b::Right) = false
37-
either_compare(a::Right, b::Left) = false
38-
either_compare(a::Left, b::Left) = a.value == b.value
53+
Base.:(==)(a::Right, b::Right) = a.value == b.value
54+
Base.:(==)(a::Left, b::Right) = false
55+
Base.:(==)(a::Right, b::Left) = false
56+
Base.:(==)(a::Left, b::Left) = a.value == b.value
3957

4058
# TypeClasses.unionall_implementationdetails(::Type{<:Either{L, R}}) where {L, R} = Either{L, R}
4159
# Traits.leaftypes(::Type{Either}) = [Either{newtype(), newtype(), Left}, Either{newtype(), newtype(), Right}]
4260
# Traits.leaftypes(::Type{Either{T}}) where T = [Either{T, newtype(), Left}, Either{T, newtype(), Right}]
4361

4462

45-
function either(left_false::L, comparison::Bool, right_true::R) where {L, R}
46-
comparison ? Right{L, R}(right_true) : Left{L, R}(left_false)
63+
function either(left_false, comparison::Bool, right_true)
64+
comparison ? Right(right_true) : Left(left_false)
4765
end
4866

49-
flip(x::Left{L, R}) where {L, R} = Right{R, L}(x.value)
50-
flip(x::Right{L, R}) where {L, R} = Left{R, L}(x.value)
67+
flip_left_right(x::Left) = Right(x.value)
68+
flip_left_right(x::Right) = Left(x.value)
5169

52-
isleft(e::Either) = either_isleft(e)
53-
either_isleft(e::Left) = true
54-
either_isleft(e::Right) = false
70+
isleft(e::Left) = true
71+
isleft(e::Right) = false
5572

56-
isright(e::Either) = either_isright(e)
57-
either_isright(e::Left) = false
58-
either_isright(e::Right) = true
73+
isright(e::Left) = false
74+
isright(e::Right) = true
5975

60-
getleft(e::Either) = either_getleft(e)
61-
either_getleft(e::Left) = e.value
62-
either_getleft(e::Right) = nothing
76+
getleft(e::Left) = e.value
77+
getleft(e::Right) = nothing
6378

64-
getright(e::Either) = either_getright(e)
65-
either_getright(e::Left) = nothing
66-
either_getright(e::Right) = e.value
79+
getright(e::Left) = nothing
80+
getright(e::Right) = e.value
6781

68-
getleftOption(e::Either) = either_getleftOption(e)
69-
either_getleftOption(e::Left{L, R}) where {L, R} = Some{L}(e.value)
70-
either_getleftOption(e::Right{L, R}) where {L, R} = None{L}()
82+
getleftOption(e::Left{L}) where {L} = Some{L}(e.value)
83+
getleftOption(e::Right) = nothing
7184

72-
getrightOption(e::Either) = either_getrightOption(e)
73-
either_getrightOption(e::Left{L, R}) where {L, R} = None{R}()
74-
either_getrightOption(e::Right{L, R}) where {L, R} = Some{R}(e.value)
85+
getrightOption(e::Left) = nothing
86+
getrightOption(e::Right{R}) where {R} = Some{R}(e.value)
7587

7688
Base.get(e::Either) = getright(e)
7789
getOption(e::Either) = getrightOption(e)
7890

7991
Base.eltype(::Type{<:Either{L, R}}) where {L, R} = R
92+
Base.eltype(::Type{<:Left{L}}) where L = Any # we have to specify this clause as otherwise we get ERROR: UndefVarError: R not defined
8093
Base.eltype(::Type{<:Either}) = Any
8194

82-
Base.iterate(e::Either, state...) = either_iterate(e, state...)
83-
either_iterate(e::Right) = e.value, nothing
84-
either_iterate(e::Right, state) = state
85-
either_iterate(e::Left) = nothing
86-
87-
Base.foreach(f, x::Either) = either_foreach(f, x)
88-
either_foreach(f, x::Right) = f(x.value); nothing
89-
either_foreach(f, x::Left) = nothing
90-
91-
Base.map(f, x::Either) = either_map(f, x)
92-
either_map(f, x::Right{L}) where {L} = Right{L}(f(x.value))
93-
function either_map(f, x::Left{L, R}) where {L, R}
94-
_R2 = Out(f, R)
95-
R2 = _R2 === NotApplicable ? Any : _R2
96-
Left{L, R2}(x.value)
97-
end
95+
Base.iterate(e::Right) = e.value, nothing
96+
Base.iterate(e::Right, state) = state
97+
Base.iterate(e::Left) = nothing
98+
99+
Base.foreach(f, x::Right) = f(x.value); nothing
100+
Base.foreach(f, x::Left) = nothing
101+
102+
Base.map(f, x::Right) = Right(f(x.value))
103+
Base.map(f, x::Left) = x
98104

99-
Iterators.flatten(e::Either) = either_flatten(e)
100-
either_flatten(x::Right) = convert(Either, x.value)
101-
either_flatten(x::Left) = x
102-
either_flatten(x::Left{L, E}) where {L, R, E <: Either{L, R}} = Left{L, R}(x.value) # just to have better type support
105+
Iterators.flatten(x::Right) = convert(Either, x.value)
106+
Iterators.flatten(x::Left) = x

src/Option.jl

Lines changed: 51 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,71 @@
1+
const Option{T} = Union{Nothing, Some{T}}
12

3+
Option{T}(a::Nothing) where T = nothing
4+
Option{T}(a::T) where T = Some{T}(a)
5+
Option(a::Nothing) = nothing
6+
Option(a::T) where T = Some{T}(a)
7+
Option{T}() where T = nothing
8+
Option() = nothing
29

3-
abstract type Option{T} end
10+
# we don't need any extra typejoin rules, as they are already provided by Base.promote_typejoin
411

5-
struct None{T} <: Option{T} end
612

7-
struct Some{T} <: Option{T}
8-
value::T
9-
end
10-
Some(value::T) where T = Some{T}(value)
13+
"""
14+
overwrite `Base.:(==)` and `Base.hash` for `Base.Some`
1115
12-
Option{T}(a::Nothing) where T = None{T}()
13-
Option{T}(a::T) where T = Some{T}(a)
14-
Option(a::Nothing) = None{Any}()
15-
Option(a::T) where T = Some{T}(a)
16-
Option{T}() where T = None{T}()
17-
Option() = None{Any}()
18-
19-
# == controversy https://github.com/JuliaLang/julia/issues/4648
20-
Base.:(==)(a::Option, b::Option) = option_compare(a, b)
21-
option_compare(a::Some, b::Some) = a.value == b.value
22-
option_compare(a::None, b::Some) = false
23-
option_compare(a::Some, b::None) = false
24-
option_compare(a::None{T}, b::None{T}) where T = true
25-
option_compare(a::None, b::None) = false
26-
# TypeClasses.unionall_implementationdetails(::Type{<:Option{T}}) where T = Option{T}
27-
# Traits.leaftypes(::Type{Option}) = [Option{newtype(), Some}, Option{newtype(), None}]
16+
as the default version is uninuitive and leads to surprising errors if you have the same intuition as me
17+
18+
Unfortunately this may breaks existing Julia code, but the semantics of Some is more a container, then a `Ref`.
19+
For further discussion see https://discourse.julialang.org/t/is-this-a-bug-some-some/39541
20+
"""
21+
22+
Base.:(==)(a::Some, b::Some) = a.value == b.value
23+
Base.hash(a::Some) = hash(a.value)
24+
25+
# we need to overwrite convert, because in the case that no conversion is possible, we currently get the super uninformative error
26+
# ERROR: could not compute non-nothing type
27+
# Stacktrace:
28+
# [1] nonnothingtype_checked(::Type) at ./some.jl:29
29+
# [2] convert(::Type{Union{Nothing, Some{T}} where T}, ::Int64) at ./some.jl:34
30+
# [3] top-level scope at none:0
31+
# importantly, we should only add clauses for Type{Option} and not Type{<:Option} to not interfere with existing code
32+
Base.convert(::Type{Option}, x) = throw(MethodError(Base.convert, (Option, x)))
33+
Base.convert(::Type{Option}, x::Option) = x
34+
# Option{T} seems to be already covered by normal Union, Some, Nothing conversions, no need to provide them
2835

2936
function iftrue(func::Function, b::Bool)
30-
b ? Some(func()) : None{Out(func)}()
37+
b ? Some(func()) : nothing
3138
end
32-
function iftrue(b::Bool, t::T) where T
33-
b ? Some{T}(t) : None{T}()
39+
function iftrue(b::Bool, t)
40+
b ? Some(t) : nothing
3441
end
3542
function iffalse(func::Function, b::Bool)
36-
!b ? Some(func()) : None{Out(func)}()
43+
!b ? Some(func()) : nothing
3744
end
38-
function iffalse(b::Bool, t::T) where T
39-
!b ? Some{T}(t) : None{T}()
45+
function iffalse(b::Bool, t)
46+
!b ? Some(t) : nothing
4047
end
4148

42-
Base.isnothing(m::Option) = option_isnothing(m)
43-
option_isnothing(m::None) = true
44-
option_isnothing(m::Some) = false
49+
issomething(m::Nothing) = false
50+
issomething(m::Some) = true
4551

46-
issomething(m::Option) = option_issomething(m)
47-
option_issomething(m::None) = false
48-
option_issomething(m::Some) = true
52+
Base.get(m::Some) = m.value
53+
Base.get(m::Nothing) = nothing
4954

50-
Base.get(m::Option) = option_get(m)
51-
option_get(m::Some) = m.value
52-
option_get(m::None) = nothing
55+
# Base.eltype is not well defined for Some, and always returns Any
56+
Base.eltype(::Type{<:Option{T}}) where {T} = T
57+
Base.eltype(::Type{Nothing}) = Any # if we don't provide this clause, we get ERROR: UndefVarError: T not defined, as the above clause matches, but no T can be infered from Nothing
5358

54-
Base.eltype(::Type{<:Option{T}}) where T = T
55-
Base.eltype(::Type{<:Option}) = Any
5659

57-
Base.iterate(o::Option, state...) = option_iterate(o, state...)
58-
option_iterate(o::Some) = o.value, nothing
59-
option_iterate(o::Some, state) = state
60-
option_iterate(o::None) = nothing
60+
Base.iterate(o::Some) = o.value, nothing
61+
Base.iterate(o::Some, state) = state
62+
Base.iterate(o::Nothing) = nothing
6163

62-
Base.foreach(f, o::Option) = option_foreach(f, o)
63-
option_foreach(f, o::Some) = f(o.value); nothing
64-
option_foreach(f, o::None) = nothing
64+
Base.foreach(f, o::Some) = f(o.value); nothing
65+
Base.foreach(f, o::Nothing) = nothing
6566

66-
Base.map(f, o::Option) = option_map(f, o)
67-
option_map(f, o::Some{T}) where T = Some(f(o.value))
68-
function option_map(f, ::None{T}) where T
69-
_T2 = Out(f, T)
70-
T2 = _T2 === NotApplicable ? Any : _T2
71-
None{T2}()
72-
end
67+
Base.map(f, o::Some) = Some(f(o.value))
68+
Base.map(f, ::Nothing) = nothing
7369

74-
Iterators.flatten(x::Option) = option_flatten(x)
75-
option_flatten(x::None) = x
76-
option_flatten(x::Some) = convert(Option, x.value)
70+
Iterators.flatten(x::Nothing) = x
71+
Iterators.flatten(x::Some) = convert(Option, x.value)

0 commit comments

Comments
 (0)