JuliaFolds / Transducers.jl

Efficient transducers for Julia
https://juliafolds.github.io/Transducers.jl/dev/
MIT License
433 stars 24 forks source link

Nested `Scan` cannot be inferred (and inference results are confusing) #14

Open tkf opened 5 years ago

tkf commented 5 years ago

This post is generated from this script: https://gist.github.com/85a0751820f2be4a6aaa707959e7cd96

Examples

using Test
using Transducers

function showingerror(f)
    err = nothing
    try
        f()
    catch err
        showerror(stdout, err)
    end
    @assert err !== nothing
end

Currently, nested Scan cannot be inferred (in most of the cases):

showingerror() do
    @inferred foldl(*, Scan(+) |> Scan(+), 1:1)
end
return type Int64 does not match inferred return type Any

Specifying initial value (init=1) does not help:

showingerror() do
    @inferred foldl(*, Scan(+) |> Scan(+), 1:1; init=1)
end
return type Int64 does not match inferred return type Any

Side note: In some REPL sessions, following actually could be inferred. But now I can't reproduce this...

foldl(*, Scan(+) |> Map(x -> x::Int) |> Scan(+), 1:1)
foldl(right, Scan(+) |> Scan(+) |> Scan(+) |> Scan(+), 1:1)

Looking into the Julia IR

Using @descend foldl(*, Scan(+) |> Scan(+), 1:1), it looks like the inference of the first example failed in

using InteractiveUtils
using Transducers: _start_init, __foldl__
rf = reducingfunction(Scan(+) |> Scan(+), *)
state = _start_init(rf, 1)
if VERSION < v"1.2-"
    @code_warntype __foldl__(rf, state, 1:1)
else
    @code_warntype optimize=true __foldl__(rf, state, 1:1)
end
Variables
  #self#::Core.Compiler.Const(Transducers.__foldl__, false)
  rf::Core.Compiler.Const(Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE}(Scan(+), Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE}(Scan(+), Transducers.BottomRF{Transducers.NOTYPE,typeof(*)}(*))), false)
  init::Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Initials.InitialOf{typeof(+)},Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Initials.InitialOf{typeof(+)},Int64}}
  arr::UnitRange{Int64}
  val@_5::Int64
  idxs::Base.OneTo{Int64}
  val@_7::TRANSDUCERS.PRIVATESTATE{TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.BOTTOMRF{TRANSDUCERS.NOTYPE,TYPEOF(*)},TRANSDUCERS.NOTYPE},TRANSDUCERS.NOTYPE},INT64,_A} WHERE _A
  result@_8::UNION{TRANSDUCERS.PRIVATESTATE{TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.BOTTOMRF{TRANSDUCERS.NOTYPE,TYPEOF(*)},TRANSDUCERS.NOTYPE},TRANSDUCERS.NOTYPE},INT64,_A} WHERE _A, TRANSDUCERS.REDUCED}
  @_9::UNION{NOTHING, TUPLE{INT64,INT64}}
  @_10::Union{}
  r#424::Union{}
  i#425::Union{}
  n#426::Union{}
  i#427::Union{}
  k@_15::Union{}
  val@_16::Union{}
  val@_17::Union{}
  i@_18::Union{}
  result@_19::Union{}
  @_20::Union{}
  r#428::Union{}
  i#429::Union{}
  n#430::Union{}
  i#431::Union{}
  val@_25::Union{}
  val@_26::Union{}
  result@_27::Union{}
  k@_28::Union{}
  i@_29::Union{}
  val@_30::Int64
  val@_31::Int64
  result@_32::UNION{TRANSDUCERS.PRIVATESTATE{TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.BOTTOMRF{TRANSDUCERS.NOTYPE,TYPEOF(*)},TRANSDUCERS.NOTYPE},TRANSDUCERS.NOTYPE},INT64,_A} WHERE _A, TRANSDUCERS.REDUCED}
  k@_33::Int64
  i@_34::Int64

Body::ANY
1 ── %1   = Base.getfield(arr, :start)::Int64
│    %2   = Base.getfield(arr, :stop)::Int64
│    %3   = Base.slt_int(%2, %1)::Bool
└───        goto #3 if not %3
2 ── %5   = Base.getfield(init, :result)::Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Initials.InitialOf{typeof(+)},Int64}
│    %6   = Base.getfield(%5, :result)::Int64
└───        return %6
3 ── %8   = Base.getfield(arr, :stop)::Int64
│    %9   = Base.getfield(arr, :start)::Int64
│    %10  = Base.Checked.checked_ssub_int(%8, %9)::Tuple{Int64,Bool}
│    %11  = Base.getfield(%10, 1)::Int64
│    %12  = Base.getfield(%10, 2)::Bool
└───        goto #5 if not %12
4 ──        invoke Base.Checked.throw_overflowerr_binaryop(:-::Symbol, %8::Int64, %9::Int64)
└───        $(Expr(:unreachable))
5 ┄─        goto #6
6 ── %17  = Base.Checked.checked_sadd_int(%11, 1)::Tuple{Int64,Bool}
│    %18  = Base.getfield(%17, 1)::Int64
│    %19  = Base.getfield(%17, 2)::Bool
└───        goto #8 if not %19
7 ──        invoke Base.Checked.throw_overflowerr_binaryop(:+::Symbol, %11::Int64, 1::Int64)
└───        $(Expr(:unreachable))
8 ┄─        goto #9
9 ──        goto #10
10 ─        goto #11
11 ─ %26  = Base.slt_int(%18, 0)::Bool
│    %27  = Base.ifelse(%26, 0, %18)::Int64
│    %28  = %new(Base.OneTo{Int64}, %27)::Base.OneTo{Int64}
└───        goto #12
12 ─        goto #13
13 ─        goto #14
14 ─        goto #18 if not false
15 ─ %33  = Base.slt_int(0, 1)::Bool
│    %34  = Base.sle_int(1, %27)::Bool
│    %35  = Base.and_int(%33, %34)::Bool
└───        goto #17 if not %35
16 ─        goto #18
17 ─        invoke Base.throw_boundserror(%28::Base.OneTo{Int64}, 1::Int64)
└───        $(Expr(:unreachable))
18 ┄        goto #19
19 ─ %41  = Base.getfield(arr, :start)::Int64
│    %42  = Base.sub_int(1, 1)::Int64
│    %43  = Base.add_int(%41, %42)::Int64
└───        goto #28 if not false
20 ─ %45  = Base.slt_int(0, 1)::Bool
└───        goto #24 if not %45
21 ─ %47  = Base.getfield(arr, :stop)::Int64
│    %48  = Base.sle_int(%43, %47)::Bool
└───        goto #23 if not %48
22 ─ %50  = Base.getfield(arr, :start)::Int64
│    %51  = Base.sle_int(%50, %43)::Bool
└───        goto #25
23 ─        goto #25
24 ─        goto #25
25 ┄ %55  = φ (#22 => %51, #23 => false, #24 => false)::Bool
└───        goto #27 if not %55
26 ─        goto #28
27 ─        invoke Base.throw_boundserror(_4::UnitRange{Int64}, 1::Int64)
└───        $(Expr(:unreachable))
28 ┄        goto #29
29 ─ %61  = %new(getfield(Transducers, Symbol("##40#41")){Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Int64}, rf, %43)::getfield(Transducers, Symbol("##40#41")){Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Int64}
│    %62  = invoke Transducers.wrapping(%61::getfield(Transducers, Symbol("##40#41")){Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Int64}, _2::Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE}, _3::Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Initials.InitialOf{typeof(+)},Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Initials.InitialOf{typeof(+)},Int64}})::UNION{TRANSDUCERS.PRIVATESTATE{TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.BOTTOMRF{TRANSDUCERS.NOTYPE,TYPEOF(*)},TRANSDUCERS.NOTYPE},TRANSDUCERS.NOTYPE},INT64,_A} WHERE _A, TRANSDUCERS.REDUCED}
│    %63  = (%62 isa Transducers.Reduced)::Bool
└───        goto #31 if not %63
30 ─ %65  = π (%62, Transducers.Reduced)
└───        return %65
31 ─ %67  = π (%62, Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Int64,_A} where _A)
└───        goto #33 if not false
32 ─        $(Expr(:unreachable))
33 ┄ %70  = Base.slt_int(%27, 0)::Bool
│    %71  = Base.ifelse(%70, 0, %27)::Int64
│    %72  = Base.sle_int(2, %71)::Bool
│    %73  = Base.ifelse(%72, %71, 1)::Int64
│    %74  = Base.slt_int(%73, 2)::Bool
└───        goto #35 if not %74
34 ─        goto #36
35 ─        goto #36
36 ┄ %78  = φ (#34 => true, #35 => false)::Bool
│    %79  = φ (#35 => 2)::Int64
│    %80  = φ (#35 => 2)::Int64
│    %81  = Base.not_int(%78)::Bool
└───        goto #59 if not %81
37 ┄ %83  = φ (#36 => %67, #58 => %120)::TRANSDUCERS.PRIVATESTATE{TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.BOTTOMRF{TRANSDUCERS.NOTYPE,TYPEOF(*)},TRANSDUCERS.NOTYPE},TRANSDUCERS.NOTYPE},INT64,_A} WHERE _A
│    %84  = φ (#36 => %79, #58 => %126)::Int64
│    %85  = φ (#36 => %80, #58 => %127)::Int64
└───        goto #41 if not false
38 ─ %87  = Base.slt_int(0, %84)::Bool
│    %88  = Base.sle_int(%84, %27)::Bool
│    %89  = Base.and_int(%87, %88)::Bool
└───        goto #40 if not %89
39 ─        goto #41
40 ─        invoke Base.throw_boundserror(%28::Base.OneTo{Int64}, %84::Int64)
└───        $(Expr(:unreachable))
41 ┄        goto #42
42 ─ %95  = Base.getfield(arr, :start)::Int64
│    %96  = Base.sub_int(%84, 1)::Int64
│    %97  = Base.add_int(%95, %96)::Int64
└───        goto #51 if not false
43 ─ %99  = Base.slt_int(0, %84)::Bool
└───        goto #47 if not %99
44 ─ %101 = Base.getfield(arr, :stop)::Int64
│    %102 = Base.sle_int(%97, %101)::Bool
└───        goto #46 if not %102
45 ─ %104 = Base.getfield(arr, :start)::Int64
│    %105 = Base.sle_int(%104, %97)::Bool
└───        goto #48
46 ─        goto #48
47 ─        goto #48
48 ┄ %109 = φ (#45 => %105, #46 => false, #47 => false)::Bool
└───        goto #50 if not %109
49 ─        goto #51
50 ─        invoke Base.throw_boundserror(_4::UnitRange{Int64}, %84::Int64)
└───        $(Expr(:unreachable))
51 ┄        goto #52
52 ─ %115 = Transducers.next(rf, %83, %97)::UNION{TRANSDUCERS.PRIVATESTATE{TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.BOTTOMRF{TRANSDUCERS.NOTYPE,TYPEOF(*)},TRANSDUCERS.NOTYPE},TRANSDUCERS.NOTYPE},INT64,_A} WHERE _A, TRANSDUCERS.REDUCED}
│    %116 = (%115 isa Transducers.Reduced)::Bool
└───        goto #54 if not %116
53 ─ %118 = π (%115, Transducers.Reduced)
└───        return %118
54 ─ %120 = π (%115, Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Int64,_A} where _A)
│    %121 = (%85 === %73)::Bool
└───        goto #56 if not %121
55 ─        goto #57
56 ─ %124 = Base.add_int(%85, 1)::Int64
└───        goto #57
57 ┄ %126 = φ (#56 => %124)::Int64
│    %127 = φ (#56 => %124)::Int64
│    %128 = φ (#55 => true, #56 => false)::Bool
│    %129 = Base.not_int(%128)::Bool
└───        goto #59 if not %129
58 ─        goto #37
59 ┄ %132 = φ (#57 => %120, #36 => %67)::TRANSDUCERS.PRIVATESTATE{TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.REDUCTION{TRANSDUCERS.SCAN{TYPEOF(+),INITIALS.INITIALOF{TYPEOF(+)}},TRANSDUCERS.BOTTOMRF{TRANSDUCERS.NOTYPE,TYPEOF(*)},TRANSDUCERS.NOTYPE},TRANSDUCERS.NOTYPE},INT64,_A} WHERE _A
│    %133 = Transducers.complete(rf, %132)::ANY
└───        return %133

The first type instability occurs at

%62  = invoke Transducers.wrapping(...)

which is inferred to return

Union{
    PrivateState{
        Reduction{
            Scan{typeof(+),Initials.InitialOf{typeof(+)}},
            Reduction{Scan{typeof(+),Initials.InitialOf{typeof(+)}},BottomRF{NOTYPE,typeof(*)},NOTYPE},
            NOTYPE,
        },
        Int64,
        _A,
    } where _A,
    Reduced,
}

where the type variable _A Julia couldn't infer (or chose to ignore?) is the inner result type (which is again a PrivateState here).

Confusingly, inferring Transducers.wrapping by (1) using @descend or by (2) running @code_warntype on next which in turn calls wrapping as below says it actually is inferrable:

using Transducers: next
@code_warntype next(rf, state, 1)
Variables
  #self#::Core.Compiler.Const(Transducers.next, false)
  rf::Core.Compiler.Const(Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE}(Scan(+), Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE}(Scan(+), Transducers.BottomRF{Transducers.NOTYPE,typeof(*)}(*))), false)
  result::Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Initials.InitialOf{typeof(+)},Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Initials.InitialOf{typeof(+)},Int64}}
  input::Int64
  #40::getfield(Transducers, Symbol("##40#41")){Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Int64}

Body::Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Int64,Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Int64,Int64}}
1 ─ %1 = Transducers.:(##40#41)::Core.Compiler.Const(getfield(Transducers, Symbol("##40#41")), false)
│   %2 = Core.typeof(rf)::Core.Compiler.Const(Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE}, false)
│   %3 = Core.typeof(input)::Core.Compiler.Const(Int64, false)
│   %4 = Core.apply_type(%1, %2, %3)::Core.Compiler.Const(getfield(Transducers, Symbol("##40#41")){Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Int64}, false)
│        (#40 = %new(%4, rf, input))
│   %6 = #40::getfield(Transducers, Symbol("##40#41")){Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Int64}
│   %7 = Transducers.wrapping(%6, rf, result)::Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Int64,Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Int64,Int64}}
└──      return %7

More confusingly, @inferred reports the result as in %62 above (similar report: https://discourse.julialang.org/t/24998)

showingerror() do
    @inferred next(rf, state, 1)
end
return type Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Int64,Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Int64,Int64}} does not match inferred return type Union{Transducers.PrivateState{Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.Reduction{Transducers.Scan{typeof(+),Initials.InitialOf{typeof(+)}},Transducers.BottomRF{Transducers.NOTYPE,typeof(*)},Transducers.NOTYPE},Transducers.NOTYPE},Int64,_A} where _A, Transducers.Reduced}

Maybe there is some threshold in the inference which stops trying to run on nested calls when the argument is too complex or something?

Version information

Julia

VERSION
v"1.3.0-DEV.525"

Transducers

print(read(`recrepo --pretty --output=- $(pathof(Transducers))`, String))
[
    {
        "is_clean": true,
        "is_clean_tracked": true,
        "revision": "4051729edeb4022f2f3cb0b74f88bdf9306a8f80",
        "toplevel": "/home/takafumi/.julia/dev/Transducers"
    }
]

This page was generated using Literate.jl.

tkf commented 4 years ago

Maybe using the opposite associativity can help here? ref https://github.com/jw3126/Setfield.jl/pull/128