I think the actual issue here is that conditional expressions do not play well with the type inference engine.
This is imo a shortcoming of the compiler (gimme more pi-nodes), and the workaround is as @rdeits said: Use control flow (if/else/@goto/early return) instead of conditional expressions. Unfortunately I’m not deep enough into the related parts of the optimizer to propose a fix, or debug the precise reason for the current approach failing to optimize this.
A smaller reproducer would be
julia> function ttt(x)
xx=x[1]
yy=x[2]
xx isa Nothing || yy isa Nothing ? nothing : (xx, yy)
end
julia> @code_typed ttt([1, nothing])
CodeInfo(
1 ─ %1 = (Base.arrayref)(true, x, 1)::Union{Nothing, Int64}
│ %2 = (Base.arrayref)(true, x, 2)::Union{Nothing, Int64}
│ %3 = (%1 isa Main.Nothing)::Bool
└── goto #3 if not %3
2 ─ goto #4
3 ─ %6 = (%2 isa Main.Nothing)::Bool
4 ┄ %7 = φ (#2 => %3, #3 => %6)::Bool
└── goto #6 if not %7
5 ─ return Main.nothing
6 ─ %10 = (Core.tuple)(%1, %2)::Tuple{Union{Nothing, Int64},Union{Nothing, Int64}}
└── return %10
) => Union{Nothing, Tuple{Union{Nothing, Int64},Union{Nothing, Int64}}}
as opposed to
julia> function ttt2(x)
xx=x[1]
yy=x[2]
xx isa Nothing && return nothing
yy isa Nothing && return nothing
return (xx, yy)
end
ttt2 (generic function with 1 method)
julia> @code_typed ttt2([1, nothing])
CodeInfo(
1 ─ %1 = (Base.arrayref)(true, x, 1)::Union{Nothing, Int64}
│ %2 = (Base.arrayref)(true, x, 2)::Union{Nothing, Int64}
│ %3 = (%1 isa Main.Nothing)::Bool
└── goto #3 if not %3
2 ─ return Main.nothing
3 ─ %6 = (%2 isa Main.Nothing)::Bool
└── goto #5 if not %6
4 ─ return Main.nothing
5 ─ %9 = π (%1, Int64)
│ %10 = π (%2, Int64)
│ %11 = (Core.tuple)(%9, %10)::Tuple{Int64,Int64}
└── return %11
) => Union{Nothing, Tuple{Int64,Int64}}
Regarding monadic style: All julia style questions are relative to the compiler: if the compiler is bad at optimizing a certain coding pattern, then it cannot be good julia style; as the compiler improves, more coding styles become viable. In my experience, the compiler is currently not very good at optimizing closures, but YMMV.