On Tuesday, August 30, 2016 3:57:36 PM CDT Yichao Yu wrote:
> And even then, this completely looses the advantage of using tuple
> (inferrable size and element types) so you shouldn't do this in general
> unless you are going to do a lot of work with the tiny tuple afterwards.
Right. If you want to grow a tuple, you should use "lispy recursion" so the
compiler can reason about the size, which is part of the type of the tuple.
(`for` loops are completely off the table for this kind of programming.)
Here's an example that builds a tuple of N `true`s (i.e., functionally
equivalent to `ntuple(d->true, N)`):
buildtrues{N}(::Type{Val{N}}) = _buildtrues((), Val{N}) # initialization
_buildtrues{N}(out::NTuple{N}, ::Type{Val{N}}) = out # termination
@inline _buildtrues{N}(out, ::Type{Val{N}}) =
_buildtrues((out..., true), Val{N}) # the "inner loop"
Key features include the `@inline` and the fact that `N` is available to the
type system. For a particular inferrable`N<15`, the compiler will just figure
out the end result and return that; the "apparent" recursion runs at compile
time, not runtime.
Note that if your condition isn't evaluatable at compile time, and especially
if it changes from one "iteration" to the next, then you're frankly much
better off using Arrays rather than tuples. Don't attempt to fake compile-time
evaluation using `Val` unless the condition is already embedded in the type
system (e.g., coming from an `::Array{T,N}`) or it's the same `Val` being used
bazillions of times. See
http://docs.julialang.org/en/latest/manual/
performance-tips/#types-with-values-as-parameters and the section after that.
This is not just a theoretical concern: while tuples have enormous advantages
in certain settings, trying to fake this with tuples/Val and getting it wrong
could easily result in a 30-100 fold performance *penalty* compared to using
Arrays.
Best,
--Tim