# Should \`min.(1,f)\` be broadcasting as \`broadcast(x-\>min(1,x),f)\`?

**URL:** <https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554>\
**Category:** Internals & Design\
**Tags:** question\
**Created:** [August 25, 2017, 8:53am UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554 "2017-08-25T08:53:41Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [August 25, 2017, 8:53am UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/1 "2017-08-25T08:53:42Z")

</div>

I ran into an issue where I expected `min.(1,f)` to be lowered to `broadcast(min,1,f)`, but instead it is lowered to `broadcast(x->min(1,x),f)`. There are a few issues with this behaviour:

1. It breaks the “semantic” contract of broadcasting.
2. Every constant needs to recompile.
3. In my case, `f` is a `Fun`, and I want to override `broadcast(::typeof(min),::Number,::Fun)` to calculate break points. This override is not called with the current behaviour. (The current approach of overriding `min(::Number,::Fun)` works, but it’s breaking the expected behaviour of `min` to return one of the arguments.)

Is there an argument in favour of the current approach? Or should I create an Issue on GitHub?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [August 25, 2017, 11:38am UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/2 "2017-08-25T11:38:47Z")

</div>

Inlining literals is significantly faster than passing them as `broadcast` arguments. Because of fusion, overloading `broadcast(::typeof(min), ...)` is not very useful anyway since your method won’t be called as soon as someone combines `min` with another dot call. e.g. your function wouldn’t be called for `min.(x, f.^2)` either.

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [August 25, 2017, 11:44am UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/3 "2017-08-25T11:44:47Z")

</div>

It’s only faster if it doesn’t have to recompile.

Yes, it won’t fuse properly, but if a user wants the correct behaviour, they’d need to write it without fusing.

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [August 25, 2017, 12:00pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/4 "2017-08-25T12:00:49Z")

</div>

But I think my biggest argument was that it’s surprising as a user. In particular, `min(1+1,f)` and `min(2,f)` dispatch differently:

```julia
julia> struct Foo end

julia> Base.broadcast(::typeof(min),x::Number,f::Foo) = "hi"

julia> min.(1,Foo())
ERROR: MethodError: no method matching isless(::Foo, ::Int64)
Closest candidates are:
  isless(::AbstractFloat, ::Real) at operators.jl:98
  isless(::Real, ::Real) at operators.jl:266
Stacktrace:
 [1] (::##1#2)(::Foo) at ./<missing>:0
 [2] broadcast(::Function, ::Foo) at ./broadcast.jl:434

julia> min.(1+3,Foo())
"hi"

```

I would suggest removing the special case, as there is still access to it via `(x->min(1,x)).(f)`.

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [August 25, 2017, 12:04pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/5 "2017-08-25T12:04:41Z")

</div>

> [@dlfivefifty](#):
>
> It’s only faster if it doesn’t have to recompile.

No recompilation will happen because it doesn’t redefine anything. It needs to be compiled as many time as it appears in the code which is consistent with everything else.

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [August 25, 2017, 12:10pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/6 "2017-08-25T12:10:59Z")

</div>

> [@dlfivefifty](#):
>
> In particular, min(1+1,f) and min(2,f) dispatch differently:

The difference is supposed to be invisible and any method definition that breaks this is invalid.

Also, if you are defining a method that does not support any code transformation done by the dot syntax, it’s a clear sign that it should just be defined as a normal function instead.

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [August 25, 2017, 12:12pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/7 "2017-08-25T12:12:56Z")

</div>

> No recompilation will happen because it doesn’t redefine anything. It needs to be compiled as many time as it appears in the code which is consistent with everything else.

I’m thinking more in the REPL usage:

```julia
julia> @time broadcast(min,2,[1,2,3])
  0.074139 seconds (48.92 k allocations: 2.768 MiB)
3-element Array{Int64,1}:
 1
 2
 2

julia> @time broadcast(min,3,[1,2,3])
  0.000093 seconds (34 allocations: 1.578 KiB)
3-element Array{Int64,1}:
 1
 2
 3

julia> @time min.(1,[1,2,3])
  0.622028 seconds (38.05 k allocations: 1.993 MiB)
3-element Array{Int64,1}:
 1
 1
 1

julia> @time min.(1,[1,2,3])
  0.025320 seconds (4.05 k allocations: 228.566 KiB)
3-element Array{Int64,1}:
 1
 1
 1

julia> @time min.(2,[1,2,3])
  0.024821 seconds (4.04 k allocations: 228.160 KiB)
3-element Array{Int64,1}:
 1
 2
 2

```

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [August 25, 2017, 12:16pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/8 "2017-08-25T12:16:35Z")

</div>

That doesn’t make any difference in real timing.

- The REPL is way slower than this compilation time.
- These are global statements so they are never assumed to be fast.

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [August 25, 2017, 12:17pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/9 "2017-08-25T12:17:22Z")

</div>

> [@yuyichao](#):
>
> The difference is supposed to be invisible and any method definition that breaks this is invalid.

In my case, `min.(1,f)` will just not work, so the choice is either it sometimes work or it never works. I’d prefer sometimes works.

A related example: I would argue that a reasonable override is

```julia
broadcast!(::typeof(*), a::Number, b::Vector) = scale!(a,b)

```

(assuming `scale!` is actually faster). Now this would not work when fused, but so what? If it’s faster sometimes, then it’s worthwhile.

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [August 25, 2017, 12:18pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/10 "2017-08-25T12:18:27Z")

</div>

Julia is not a static compiled language. The REPL is real timing for a lot of people.

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [August 25, 2017, 12:23pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/11 "2017-08-25T12:23:07Z")

</div>

> [@dlfivefifty](#):
>
> In my case, min.(1,f) will just not work, so the choice is either it sometimes work or it never works. I’d prefer sometimes works.

So don’t redefine `broadcast` as I mentioned?

> [@dlfivefifty](#):
>
> broadcast!(::typeof(\*), a::Number, b::Vector) = scale!(a,b)

I hope you mean Vector first, but

1. No it won’t be faster (if it is, it’s a performance bug).
2. If you are just defining optimization, it is perfectly valid and you are not supposed to be able to observe the difference.

> [@dlfivefifty](#):
>
> Julia is not a static compiled language.

The high performance part of julia [**is** a statically compiled language](https://discourse.julialang.org/t/notes-on-the-julia-compiler-jit-vs-static/4275/2).

> [@dlfivefifty](#):
>
> The REPL is real timing for a lot of people.

That’s exactly why we document and comment in all different places so that people don’t do this.

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [August 25, 2017, 12:28pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/12 "2017-08-25T12:28:32Z")

</div>

> [@yuyichao](#):
>
> So don’t redefine broadcast as I mentioned?

One needs to be able to override `broadcast` to use generic code (e.g., DifferentialEquations.jl).

Besides, it wasn’t my idea to override `broadcast`, I just merged a pull request from @stevengj

My point is less about this particular usage, but rather this sort of optimization is morally equivalent to compiler optimizations (a la Matlab), where the speed and behaviour of `min.(1,f)` is inconsistent with the implied lowering to `broadcast(min,1,f)`.

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [August 25, 2017, 12:50pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/13 "2017-08-25T12:50:56Z")

</div>

> [@dlfivefifty](#):
>
> where the speed and behaviour of min.(1,f) is inconsistent with the implied lowering to broadcast(min,1,f).

That is never meant to be the implied lowering.

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [August 25, 2017, 12:53pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/14 "2017-08-25T12:53:15Z")

</div>

To quote [https://docs.julialang.org/en/release-0.6/manual/functions/#man-vectorized-1:](https://docs.julialang.org/en/release-0.6/manual/functions/#man-vectorized-1:)

> [@](#):
>
> More generally, f.(args…) is actually equivalent to broadcast(f, args…)

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [August 25, 2017, 1:37pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/15 "2017-08-25T13:37:48Z")

</div>

That doc can be clarified for it excludes fusing.  
In general, the lowering should be allowed to do any transformation that’s valid for the base broadcast implementation.

---

<div class="post-metadata">

**Author:** ![PabloZubieta](https://avatars.discourse-cdn.com/v4/letter/p/ee7513/32.png) [@PabloZubieta](https://discourse.julialang.org/u/PabloZubieta)\
**Post date:** [August 25, 2017, 1:37pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/16 "2017-08-25T13:37:56Z")

</div>

> More generally, f.(args…) is actually equivalent to broadcast(f, args…)

That relation implies (and the underlying implementation assumes) that `f` is defined for the element types (or type if not with a size) of the container-like objects in `args`.

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [August 25, 2017, 1:46pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/17 "2017-08-25T13:46:10Z")

</div>

I opened up an issue to fix the docs:

[https://github.com/JuliaLang/julia/issues/23445](https://github.com/JuliaLang/julia/issues/23445)

---

<div class="post-metadata">

**Author:** ![PabloZubieta](https://avatars.discourse-cdn.com/v4/letter/p/ee7513/32.png) [@PabloZubieta](https://discourse.julialang.org/u/PabloZubieta)\
**Post date:** [August 25, 2017, 2:03pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/18 "2017-08-25T14:03:01Z")

</div>

Thinking about this a bit more, there might be a way to make the literal transformation after parsing, so if someone overwrites `broadcast(::typeof(f), ...)` it will get called when no dot-fusion occurs.

The only problem is that it can prevent that a broadcast call with a lot of arguments gets inferred (given the [restrictions](https://github.com/JuliaLang/julia/blob/master/base/inference.jl#L5) to avoid handling potentially infinite types). But this is a already a [problem](https://github.com/JuliaLang/julia/issues/22255) anyway.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [August 25, 2017, 8:41pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/19 "2017-08-25T20:41:49Z")

</div>

> [@dlfivefifty](#):
>
> Besides, it wasn’t my idea to override broadcast, I just merged a pull request from @stevengj

If I recall correctly, I suggested that you override `broadcast` for arbitrary functions `f`, so that it works with fusion etcetera…

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [August 25, 2017, 8:50pm UTC](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554/20 "2017-08-25T20:50:28Z")

</div>

Yes that’s what’s implemented. But it doesn’t work for functions that require break points, like `abs.(x)` or `min.(1,x)`.

The latter example is challenging because the documentation for `min` says it should return one of the inputs, so the current pointwise implementation of `min(1,f)` is possibly confusing.

[Next page](https://discourse.julialang.org/t/should-min-1-f-be-broadcasting-as-broadcast-x-min-1-x-f/5554.md?page=2)
