Skip to content

Commit 5952345

Browse files
S. SahmS. Sahm
authored andcommitted
switched to use more restricted version of flatten, which does not use nested dispatch
instead we now use Base.convert() for elementwise interactions between nested containers
1 parent 8a0d55e commit 5952345

7 files changed

Lines changed: 58 additions & 73 deletions

File tree

src/ContextManager.jl

Lines changed: 9 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
"""
22
As ContextManager we denote a computation which has a pre-computation and possibly a cleaning up step
33
when run with x->x it is supposed to return x.
4-
5-
Because Julia's typeinference is and stays inaccurate, we cannot really work with TypeTags denoting the elementtype.
6-
The reason is that for a ContextManager there is no way to get from a ``ContextManager{ElemType=Any}`` to a
7-
``ContextManager{ElemType=ContextManager}`` in case Any was just ContextManager. This runtime type cast is impossible
8-
because we would have to run the contextmanager for it, which we don`t want to.
9-
10-
Hence we fallback to using runtime approach, and don't need the elemtype as typetag
114
"""
125

136
"""
@@ -35,47 +28,16 @@ macro ContextManager(func)
3528
end
3629
end
3730

38-
"""
39-
we denote flattening by simply checking for this wrapper when executing a ContextManager
40-
41-
if it is FlattenMe{ContextManager}, we will flatten it out, otherwise the FlattenMe will be output
42-
so that other monads can flatten it later
43-
"""
44-
struct FlattenMe{T}
45-
value::T
46-
end
47-
48-
4931
# ContextManager is just a wrapper
5032
# pass through function call syntax
51-
# in addition we do flattening at runtime
52-
function (c::ContextManager)(cont)
53-
# in words:
54-
# cont is the function provided from the outside,
55-
# i.e. think about calling this resulting context with cont=identity ``resultingcontext(identity)``
56-
57-
# flattening something means that the context managers are executed in order
58-
# first both preparations in order (1, 2) and then both cleanups in reverse order (2, 1)
59-
# if no inner contextmanager is to be called, we found the inner value to return outside
60-
function recfunc(inner)
61-
if inner isa FlattenMe{<:ContextManager}
62-
# TODO check whether inner.value(recfunc) is lethal
63-
# .f should be safer, i.e. lead to less recursion
64-
inner.value.f(recfunc)
65-
else
66-
cont(inner)
67-
end
68-
end
69-
c.f(recfunc)
70-
end
33+
(c::ContextManager)(cont) = c.f(cont)
7134

7235

7336
function Base.eltype(::Type{<:ContextManager{F}}) where F
7437
Out(apply, F, typeof(identity))
7538
end
7639
Base.eltype(::Type{<:ContextManager}) = Any
7740

78-
7941
function Base.foreach(f, c::ContextManager)
8042
Base.map(f, c)(x -> x)
8143
nothing
@@ -87,3 +49,11 @@ end
8749

8850
# we do flatten via a mere TypeWrapper to be able to do flattening at runtime if type inference is not good enough
8951
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
58+
end
59+
end

src/DataTypesBasic.jl

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

32-
export @overwrite_Base, @overwrite_Some,
32+
export @overwrite_Some,
3333
Const,
3434
Identity,
3535
Option, None, Some, issomething, iftrue, iffalse, getOption, # isnothing comes from Base
@@ -45,6 +45,7 @@ include("Option.jl")
4545
include("Try.jl")
4646
include("Either.jl")
4747
include("ContextManager.jl")
48+
include("convert.jl")
4849

4950
macro overwrite_Base()
5051
esc(quote

src/Either.jl

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,6 @@ end
4949
flip(x::Left{L, R}) where {L, R} = Right{R, L}(x.value)
5050
flip(x::Right{L, R}) where {L, R} = Left{R, L}(x.value)
5151

52-
Base.get(e::Either) = either_get(e)
53-
either_get(e::Left) = nothing
54-
either_get(e::Right) = e.value
55-
56-
getOption(e::Either) = either_getOption(e)
57-
either_getOption(::Left{L, R}) where {L, R} = None{R}()
58-
either_getOption(e::Right{L, R}) where {L, R} = Some{R}(e.value)
59-
6052
isleft(e::Either) = either_isleft(e)
6153
either_isleft(e::Left) = true
6254
either_isleft(e::Right) = false
@@ -81,11 +73,17 @@ getrightOption(e::Either) = either_getrightOption(e)
8173
either_getrightOption(e::Left{L, R}) where {L, R} = None{R}()
8274
either_getrightOption(e::Right{L, R}) where {L, R} = Some{R}(e.value)
8375

84-
Base.convert(::Type{<:Option}, e::Either) = getrightOption(e)
76+
Base.get(e::Either) = getright(e)
77+
getOption(e::Either) = getrightOption(e)
8578

8679
Base.eltype(::Type{<:Either{L, R}}) where {L, R} = R
8780
Base.eltype(::Type{<:Either}) = Any
8881

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+
8987
Base.foreach(f, x::Either) = either_foreach(f, x)
9088
either_foreach(f, x::Right) = f(x.value); nothing
9189
either_foreach(f, x::Left) = nothing
@@ -99,19 +97,6 @@ function either_map(f, x::Left{L, R}) where {L, R}
9997
end
10098

10199
Iterators.flatten(e::Either) = either_flatten(e)
102-
either_flatten(x::Right{L, <:Either}) where {L} = x.value
103-
either_flatten(x::Right{L, Any}) where {L} = Base.Iterators.flatten(Right{L}(x.value))
100+
either_flatten(x::Right) = convert(Either, x.value)
104101
either_flatten(x::Left) = x
105102
either_flatten(x::Left{L, E}) where {L, R, E <: Either{L, R}} = Left{L, R}(x.value) # just to have better type support
106-
107-
108-
# transform Option/Try to Either
109-
getEither(v::Option) = option_getEither(v)
110-
option_getEither(v::Some) = Right{Any}(v.value)
111-
option_getEither(v::None{T}) where T = Left{Nothing, T}(nothing)
112-
113-
getEither(v::Try) = try_getEither(v)
114-
try_getEither(v::Success) = Right{Any}(v.value)
115-
try_getEither(v::Failure{T}) where T = Left{Failure, T}(v)
116-
117-
getEither(v::Either) = v

src/Identity.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,9 @@ end
55
Base.get(a::Identity) = a.value
66
Base.eltype(::Type{<:Identity{T}}) where T = T
77
Base.eltype(::Type{<:Identity}) = Any
8+
9+
Base.iterate(a::Identity) = a.value, nothing
10+
Base.iterate(a::Identity, state) = state
11+
Base.foreach(f, a::Identity) = begin f(a); nothing; end
812
Base.map(f, a::Identity) = Identity(f(a.value))
9-
Base.Iterators.flatten(a::Identity) = a.value
13+
Base.Iterators.flatten(a::Identity) = convert(Identity, a.value)

src/Option.jl

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,4 @@ end
7373

7474
Iterators.flatten(x::Option) = option_flatten(x)
7575
option_flatten(x::None) = x
76-
option_flatten(x::Some{<:Option}) = x.value
77-
option_flatten(x::Some{Any}) = Iterators.flatten(Some{typeof(x.value)}(x.value)) # this fixes the type-information which will never return Any
78-
79-
# getOption tries to transform into Option
80-
getOption(v::Option) = v
76+
option_flatten(x::Some) = convert(Option, x.value)

src/Try.jl

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,6 @@ getOption(t::Try) = try_getOption(t)
117117
try_getOption(t::Success) = Some(t.value)
118118
try_getOption(::Failure{T}) where T = None{T}()
119119

120-
Base.convert(::Type{<:Option}, t::Try) = getOption(t)
121-
122120
Base.eltype(::Type{<:Try{T}}) where T = T
123121
Base.eltype(::Type{<:Try}) = Any
124122

@@ -141,8 +139,7 @@ end
141139

142140
Iterators.flatten(t::Try) = try_flatten(t)
143141
try_flatten(x::Failure) = x
144-
try_flatten(x::Success{<:Try}) = x.value
145-
try_flatten(x::Success{Any}) = Iterators.flatten(Success(x.value))
142+
try_flatten(x::Success) = convert(Try, x.value)
146143

147144

148145
# support for combining exceptions

src/convert.jl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# conversions among Identity, Option, Try, Either, Vector
2+
# =======================================================
3+
4+
# Identity
5+
Base.convert(::Type{<:Option}, x::Identity) = Option(x.value)
6+
Base.convert(::Type{<:Try}, x::Identity) = Success(x.value)
7+
Base.convert(::Type{<:Either}, x::Identity) = Right(x.value)
8+
Base.convert(::Type{<:Vector}, x::Identity) = [x.value]
9+
10+
# Option
11+
Base.convert(::Type{<:Vector}, x::Option) = option_asVector(x)
12+
option_asVector(x::Some) = [x.value]
13+
option_asVector(x::None) = []
14+
Base.convert(::Type{<:Either}, x::Option) = option_asEither(x)
15+
option_asEither(x::Some) = Right{Nothing}(x.value)
16+
option_asEither(x::None{T}) where T = Left{Nothing, T}(nothing)
17+
18+
19+
# Try
20+
Base.convert(::Type{<:Option}, x::Try) = getOption(x)
21+
Base.convert(::Type{<:Either}, x::Try) = try_asEither(x)
22+
try_asEither(x::Success) = Right{Failure}(x.value)
23+
try_asEither(x::Failure{T}) where T = Left{Failure, T}(x.value)
24+
Base.convert(::Type{<:Vector}, x::Try) = try_asVector(x)
25+
try_asVector(t::Success) = [t.value]
26+
try_asVector(t::Failure) = []
27+
28+
# Either
29+
Base.convert(::Type{<:Option}, e::Either) = getOption(e)
30+
Base.convert(::Type{<:Vector}, e::Either) = either_asVector(e)
31+
either_asVector(e::Right) = [e.value]
32+
either_asVector(e::Left) = []

0 commit comments

Comments
 (0)