@@ -5,12 +5,10 @@ module Broadcast
5
5
using Base. Cartesian
6
6
using Base: promote_eltype_op, linearindices, tail, OneTo, to_shape,
7
7
_msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache,
8
- nullable_returntype, null_safe_eltype_op, hasvalue
8
+ nullable_returntype, null_safe_eltype_op, hasvalue, is_nullable_array
9
9
import Base: broadcast, broadcast!
10
10
export broadcast_getindex, broadcast_setindex!, dotview
11
11
12
- typealias ScalarType Union{Type{Any}, Type{Nullable}}
13
-
14
12
# # Broadcasting utilities ##
15
13
# fallbacks for some special cases
16
14
@inline broadcast (f, x:: Number... ) = f (x... )
@@ -30,28 +28,37 @@ containertype(::Type) = Any
30
28
containertype {T<:Ptr} (:: Type{T} ) = Any
31
29
containertype {T<:Tuple} (:: Type{T} ) = Tuple
32
30
containertype {T<:Ref} (:: Type{T} ) = Array
33
- containertype {T<:AbstractArray} (:: Type{T} ) = Array
31
+ containertype {T<:AbstractArray} (:: Type{T} ) =
32
+ is_nullable_array (T) ? Array{Nullable} : Array
34
33
containertype {T<:Nullable} (:: Type{T} ) = Nullable
35
34
containertype (ct1, ct2) = promote_containertype (containertype (ct1), containertype (ct2))
36
35
@inline containertype (ct1, ct2, cts... ) = promote_containertype (containertype (ct1), containertype (ct2, cts... ))
37
36
38
37
promote_containertype (:: Type{Array} , :: Type{Array} ) = Array
39
38
promote_containertype (:: Type{Array} , ct) = Array
40
39
promote_containertype (ct, :: Type{Array} ) = Array
41
- promote_containertype (:: Type{Tuple} , :: ScalarType ) = Tuple
42
- promote_containertype (:: ScalarType , :: Type{Tuple} ) = Tuple
40
+ promote_containertype (:: Type{Tuple} , :: Type{Any} ) = Tuple
41
+ promote_containertype (:: Type{Any} , :: Type{Tuple} ) = Tuple
43
42
promote_containertype (:: Type{Any} , :: Type{Nullable} ) = Nullable
44
43
promote_containertype (:: Type{Nullable} , :: Type{Any} ) = Nullable
44
+ promote_containertype (:: Type{Nullable} , :: Type{Array} ) = Array{Nullable}
45
+ promote_containertype (:: Type{Array} , :: Type{Nullable} ) = Array{Nullable}
46
+ promote_containertype (:: Type{Array{Nullable}} , :: Type{Array{Nullable}} ) =
47
+ Array{Nullable}
48
+ promote_containertype (:: Type{Array{Nullable}} , :: Type{Array} ) = Array{Nullable}
49
+ promote_containertype (:: Type{Array} , :: Type{Array{Nullable}} ) = Array{Nullable}
50
+ promote_containertype (:: Type{Array{Nullable}} , ct) = Array{Nullable}
51
+ promote_containertype (ct, :: Type{Array{Nullable}} ) = Array{Nullable}
45
52
promote_containertype {T} (:: Type{T} , :: Type{T} ) = T
46
53
47
54
# # Calculate the broadcast indices of the arguments, or error if incompatible
48
55
# array inputs
49
56
broadcast_indices () = ()
50
57
broadcast_indices (A) = broadcast_indices (containertype (A), A)
51
- broadcast_indices (:: ScalarType , A) = ()
58
+ broadcast_indices (:: Union{Type{Any}, Type{Nullable}} , A) = ()
52
59
broadcast_indices (:: Type{Tuple} , A) = (OneTo (length (A)),)
53
60
broadcast_indices (:: Type{Array} , A:: Ref ) = ()
54
- broadcast_indices (:: Type{Array } , A) = indices (A)
61
+ broadcast_indices {T<:Array} (:: Type{T } , A) = indices (A)
55
62
@inline broadcast_indices (A, B... ) = broadcast_shape ((), broadcast_indices (A), map (broadcast_indices, B)... )
56
63
# shape (i.e., tuple-of-indices) inputs
57
64
broadcast_shape (shape:: Tuple ) = shape
125
132
126
133
Base. @propagate_inbounds _broadcast_getindex (A, I) = _broadcast_getindex (containertype (A), A, I)
127
134
Base. @propagate_inbounds _broadcast_getindex (:: Type{Array} , A:: Ref , I) = A[]
128
- Base. @propagate_inbounds _broadcast_getindex (:: ScalarType , A, I) = A
135
+ Base. @propagate_inbounds _broadcast_getindex (:: Type{Any} , A, I) = A
136
+ Base. @propagate_inbounds _broadcast_getindex (:: Union {Type{Any},
137
+ Type{Nullable}}, A, I) = A
129
138
Base. @propagate_inbounds _broadcast_getindex (:: Any , A, I) = A[I]
130
139
131
140
# # Broadcasting core
@@ -276,20 +285,28 @@ ftype(f, A) = typeof(f)
276
285
ftype (f, A... ) = typeof (a -> f (a... ))
277
286
ftype (T:: Type , A... ) = Type{T}
278
287
279
- typestuple (a) = (Base. @_pure_meta ; Tuple{eltype (a)})
280
- typestuple (T:: Type ) = (Base. @_pure_meta ; Tuple{Type{T}})
281
- typestuple (a, b... ) = (Base. @_pure_meta ; Tuple{typestuple (a). types... , typestuple (b... ). types... })
288
+ # nullables need to be treated like scalars sometimes and like containers
289
+ # other times, so there are two variants of typestuple.
290
+
291
+ # if the first argument is Any, then Nullable should be treated like a
292
+ # scalar; if the first argument is Array, then Nullable should be treated
293
+ # like a container.
294
+ typestuple (:: Type , a) = (Base. @_pure_meta ; Tuple{eltype (a)})
295
+ typestuple (:: Type{Any} , a:: Nullable ) = (Base. @_pure_meta ; Tuple{typeof (a)})
296
+ typestuple (:: Type , T:: Type ) = (Base. @_pure_meta ; Tuple{Type{T}})
297
+ typestuple {T} (:: Type{T} , a, b... ) = (Base. @_pure_meta ; Tuple{typestuple (T, a). types... , typestuple (T, b... ). types... })
282
298
283
- ziptype (A) = typestuple (A)
284
- ziptype (A, B) = (Base. @_pure_meta ; Iterators. Zip2{typestuple (A), typestuple (B)})
285
- @inline ziptype (A, B, C, D... ) = Iterators. Zip{typestuple (A), ziptype (B, C, D... )}
299
+ # these functions take the variant of typestuple to be used as first argument
300
+ ziptype {T} (:: Type{T} , A) = typestuple (T, A)
301
+ ziptype {T} (:: Type{T} , A, B) = (Base. @_pure_meta ; Iterators. Zip2{typestuple (T, A), typestuple (T, B)})
302
+ @inline ziptype {T} (:: Type{T} , A, B, C, D... ) = Iterators. Zip{typestuple (T, A), ziptype (T, B, C, D... )}
286
303
287
- _broadcast_type ( f, T:: Type , As... ) = Base. _return_type (f, typestuple (T, As... ))
288
- _broadcast_type ( f, A, Bs... ) = Base. _default_eltype (Base. Generator{ziptype (A, Bs... ), ftype (f, A, Bs... )})
304
+ _broadcast_type {S} ( :: Type{S} , f, T:: Type , As... ) = Base. _return_type (f, typestuple (S, T, As... ))
305
+ _broadcast_type {T} ( :: Type{T} , f, A, Bs... ) = Base. _default_eltype (Base. Generator{ziptype (T, A, Bs... ), ftype (f, A, Bs... )})
289
306
290
307
# broadcast methods that dispatch on the type of the final container
291
308
@inline function broadcast_c (f, :: Type{Array} , A, Bs... )
292
- T = _broadcast_type (f, A, Bs... )
309
+ T = _broadcast_type (Any, f, A, Bs... )
293
310
shape = broadcast_indices (A, Bs... )
294
311
iter = CartesianRange (shape)
295
312
if isleaftype (T)
@@ -300,14 +317,21 @@ _broadcast_type(f, A, Bs...) = Base._default_eltype(Base.Generator{ziptype(A, Bs
300
317
end
301
318
return broadcast_t (f, Any, shape, iter, A, Bs... )
302
319
end
320
+ @inline function broadcast_c (f, :: Type{Array{Nullable}} , A, Bs... )
321
+ @inline rec (x) = broadcast (f, x)
322
+ @inline rec (x, y) = broadcast (f, x, y)
323
+ @inline rec (x, y, z) = broadcast (f, x, y, z)
324
+ @inline rec (xs... ) = broadcast (f, xs... )
325
+ broadcast_c (rec, Array, A, Bs... )
326
+ end
303
327
function broadcast_c (f, :: Type{Tuple} , As... )
304
328
shape = broadcast_indices (As... )
305
329
n = length (shape[1 ])
306
330
return ntuple (k-> f ((_broadcast_getindex (A, k) for A in As). .. ), n)
307
331
end
308
332
@inline function broadcast_c (f, :: Type{Nullable} , a... )
309
333
nonnull = all (hasvalue, a)
310
- S = _broadcast_type (f, a... )
334
+ S = _broadcast_type (Array, f, a... )
311
335
if isleaftype (S) && null_safe_eltype_op (f, a... )
312
336
Nullable {S} (f (map (unsafe_get, a)... ), nonnull)
313
337
else
@@ -323,21 +347,28 @@ end
323
347
"""
324
348
broadcast(f, As...)
325
349
326
- Broadcasts the arrays, tuples, `Ref`s , nullables, and/or scalars `As` to a
350
+ Broadcasts the arrays, tuples, `Ref`, nullables, and/or scalars `As` to a
327
351
container of the appropriate type and dimensions. In this context, anything
328
- that is not a subtype of `AbstractArray`, `Ref` (except for `Ptr`s), `Tuple`,
352
+ that is not a subtype of `AbstractArray`, `Ref` (except for `Ptr`s) or `Tuple`,
329
353
or `Nullable` is considered a scalar. The resulting container is established by
330
354
the following rules:
331
355
332
356
- If all the arguments are scalars, it returns a scalar.
333
357
- If the arguments are tuples and zero or more scalars, it returns a tuple.
334
- - If the arguments contain at least one array or `Ref`, it returns an array
335
- (expanding singleton dimensions), and treats `Ref`s as 0-dimensional arrays,
336
- and tuples as a 1-dimensional arrays .
358
+ - If there is at least an array or a `Ref` in the arguments , it returns an array
359
+ (and treats any `Ref` as a 0-dimensional array of its contents and any tuple
360
+ as a 1-dimensional array) expanding singleton dimensions .
337
361
338
- The following additional rule applies to `Nullable` arguments: If there is at
339
- least one `Nullable`, and all the arguments are scalars or `Nullable`, it
340
- returns a `Nullable` treating `Nullable`s as "containers".
362
+ The following additional rules apply to `Nullable` arguments:
363
+
364
+ - If there is at least a `Nullable`, and all the arguments are scalars or
365
+ `Nullable`, it returns a `Nullable`.
366
+ - If there is at least an array or a `Ref` with `Nullable` entries, or there
367
+ is at least an array or a `Ref` (perhaps with scalar entries instead of
368
+ `Nullable` entries) and a nullable, then the result is an array of
369
+ `Nullable` entries.
370
+ - If there is a tuple and a nullable, the result is an error, as this case is
371
+ not currently supported.
341
372
342
373
A special syntax exists for broadcasting: `f.(args...)` is equivalent to
343
374
`broadcast(f, args...)`, and nested `f.(g.(args...))` calls are fused into a
@@ -402,8 +433,21 @@ Nullable{String}("XY")
402
433
julia> broadcast(/, 1.0, Nullable(2.0))
403
434
Nullable{Float64}(0.5)
404
435
405
- julia> (1 + im) ./ Nullable{Int}()
406
- Nullable{Complex{Float64}}()
436
+ julia> [Nullable(1), Nullable(2), Nullable()] .* 3
437
+ 3-element Array{Nullable{Int64},1}:
438
+ 3
439
+ 6
440
+ #NULL
441
+
442
+ julia> [1+im, 2+2im, 3+3im] ./ Nullable{Int}()
443
+ 3-element Array{Nullable{Complex{Float64}},1}:
444
+ #NULL
445
+ #NULL
446
+ #NULL
447
+
448
+ julia> Ref(7) .+ Nullable(3)
449
+ 0-dimensional Array{Nullable{Int64},0}:
450
+ 10
407
451
```
408
452
"""
409
453
@inline broadcast (f, A, Bs... ) = broadcast_c (f, containertype (A, Bs... ), A, Bs... )
0 commit comments