# Why \`front\` and \`tail\` are defined for only \`Tuple\`?

**URL:** <https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961>\
**Category:** Internals & Design\
**Created:** [July 23, 2023, 1:37pm UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961 "2023-07-23T13:37:58Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![rmsmsgood](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rmsmsgood/32/20544_2.png) [@rmsmsgood](https://discourse.julialang.org/u/rmsmsgood)\
**Post date:** [July 23, 2023, 1:37pm UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961/1 "2023-07-23T13:37:58Z")

</div>

[https://docs.julialang.org/en/v1/base/collections/#Base.front](https://docs.julialang.org/en/v1/base/collections/#Base.front)

I expect below, but `MethodError` raised:

```julia
julia> x = [3, 5, 7, 4, 0, 9];
julia> Base.front(x)
[3,5,7,4,0]

```

Of course `x[Not(end)]` is also great choice if we are using `DataFrames.jl`, or just `x[(end-1):end]` is OK. But maybe `front` and `tail` are very useful like `first` and `last`. Is there any reason why the functions are not defined for other collections, for instance, performance issue?

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [July 23, 2023, 2:39pm UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961/2 "2023-07-23T14:39:46Z")

</div>

I don’t know about historical reasons, but generalizing this is a little tough. For tuples, they are not allocating - the compiler knows exactly the type of Tuple it will return based on the argument. But to do this for a Vector, you’d either need to make a new container or return a surprise `View`.

Edit: one other option is a mutation with `pop!` or `popfirst!` … But we already have those functions 😅

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [July 23, 2023, 3:50pm UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961/3 "2023-07-23T15:50:57Z")

</div>

`first` and `last` apply generically to iterators. `Base.front` is basically the same as `first`, but `Base.tail` might not work as expected for all iterators.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [July 23, 2023, 6:19pm UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961/4 "2023-07-23T18:19:30Z")

</div>

> [@rmsmsgood](#):
>
> Of course `x[Not(end)]` is also great choice

Be very careful with this `Not` thing, assuming it comes from InvertedIndices.jl. Its implementation is very inefficient, compare:

```julia
# manual indexing, baseline:
julia> @btime $x[begin:end-1]
  109.519 ns (1 allocation: 96 bytes)

# Not(): 30x slower!
julia> using InvertedIndices
julia> @btime $x[Not(end)]
  2.961 μs (35 allocations: 1.14 KiB)

# there's a readable zero-overhead solution in Accessors:
# @delete x[end]
# or
# @delete last(x)
julia> using Accessors
julia> @btime @delete $x[end]
  95.899 ns (1 allocation: 112 bytes)
julia> @btime @delete last($x)
  95.795 ns (1 allocation: 112 bytes)

```

But yeah, anyway a more general `front`/`tail` in Base would be nice.

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [July 23, 2023, 7:53pm UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961/5 "2023-07-23T19:53:10Z")

</div>

> [@aplavin](#):
>
> `julia> @btime @delete last($x)`

That `last` optic seems sorta crazy. What is happening there?

---

<div class="post-metadata">

**Author:** ![rmsmsgood](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rmsmsgood/32/20544_2.png) [@rmsmsgood](https://discourse.julialang.org/u/rmsmsgood)\
**Post date:** [July 24, 2023, 1:44am UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961/6 "2023-07-24T01:44:27Z")

</div>

Wow, good to know! Thank you!! But `Not` is very convenient 😂… I have a question, do your alternative solution actually changes original `x`?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 24, 2023, 2:18am UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961/7 "2023-07-24T02:18:45Z")

</div>

I’ve only seen these methods used commonly for recursive inlined Tuple iteration. If comprehension was type stable for tuples it’d probably be used instead.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [July 24, 2023, 6:33am UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961/8 "2023-07-24T06:33:12Z")

</div>

> [@jar1](#):
>
> That `last` optic seems sorta crazy. What is happening there?

No magic, just [https://github.com/JuliaObjects/Accessors.jl/blob/master/src/functionlenses.jl#L6](https://github.com/JuliaObjects/Accessors.jl/blob/master/src/functionlenses.jl#L6) (:

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [July 24, 2023, 6:37am UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961/9 "2023-07-24T06:37:43Z")

</div>

> [@rmsmsgood](#):
>
> But `Not` is very convenient 😂…

Well, Accessors are also very convenient — but more general and efficient!

> [@rmsmsgood](#):
>
> I have a question, do your alternative solution actually changes original `x`?

No, all Accessors operations create a new object and keep the original one intact.  
So, it’s used as

```julia
...
y = @delete last(x)
...

```

and not as

```julia
...
@delete last(x)
...

```

---

<div class="post-metadata">

**Author:** ![rmsmsgood](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rmsmsgood/32/20544_2.png) [@rmsmsgood](https://discourse.julialang.org/u/rmsmsgood)\
**Post date:** [December 26, 2023, 1:40am UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961/10 "2023-12-26T01:40:57Z")

</div>

Recently I use tensor so many times so I implement `head`(same to `front` in this thread) and `tail` by meta programming. I wish one could save time using my functions.

```julia-auto
head(x) = eval(Meta.parse("$(x)[$(":, " ^ (length(size(x))-1)) 1:(end-1)]"))
tail(x) = eval(Meta.parse("$(x)[$(":, " ^ (length(size(x))-1)) 2: end]"))

```

`head` and `tail` detect last dimension of array and drop a last or first slice in the dimenson.

### Example

**vector:**

```julia-auto
julia> @show X = rand(0:9, 10);
X = rand(0:9, 10) = [3, 9, 0, 2, 8, 6, 8, 9, 2, 9]

julia> @show head(X);
head(X) = [3, 9, 0, 2, 8, 6, 8, 9, 2]

julia> @show tail(X);
tail(X) = [9, 0, 2, 8, 6, 8, 9, 2, 9]

```

**matrix:**

```julia-auto
julia> X_ = rand(0:9, 3, 3)
3×3 Matrix{Int64}:
 0 8 7
 0 5 8
 6 4 1

julia> head(X_)
3×2 Matrix{Int64}:
 0 8
 0 5
 6 4

julia> tail(X_)
3×2 Matrix{Int64}:
 8 7
 5 8
 4 1

```

**3-tensor:**

```julia-auto
julia> X__ = rand(0:1, 3, 3, 3)
3×3×3 Array{Int64, 3}:
[:, :, 1] =
 1 0 0
 1 1 1
 1 0 1

[:, :, 2] =
 1 0 1
 1 1 0
 1 0 1

[:, :, 3] =
 0 0 0
 0 1 0
 0 1 1

julia> head(X__)
3×3×2 Array{Int64, 3}:
[:, :, 1] =
 1 0 0
 1 1 1
 1 0 1

[:, :, 2] =
 1 0 1
 1 1 0
 1 0 1

julia> tail(X__)
3×3×2 Array{Int64, 3}:
[:, :, 1] =
 1 0 1
 1 1 0
 1 0 1

[:, :, 2] =
 0 0 0
 0 1 0
 0 1 1

```

**vector of vector:**

```julia-auto
julia> head([[2, 3], [4, 6, 7], ["s"]])
2-element Vector{Vector}:
 [2, 3]
 [4, 6, 7]

julia> tail([[2, 3], [4, 6, 7], ["s"]])
2-element Vector{Vector}:
 [4, 6, 7]
 ["s"]

```

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [December 29, 2023, 9:56pm UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961/11 "2023-12-29T21:56:29Z")

</div>

I’m not sure if you need to do metaprogramming for this.

```julia-repl
julia> head(x::AbstractArray{T,N}) where {T,N} = x[ntuple(i->Colon(),Val{N-1}())..., begin:end-1]
head (generic function with 1 method)

julia> tail(x::AbstractArray{T,N}) where {T,N} = x[ntuple(i->Colon(),Val{N-1}())..., begin+1:end]
tail (generic function with 1 method)

julia> head(1:5)                                                      
 1:4

julia> tail(1:5)                                                     
 2:5

```

---

<div class="post-metadata">

**Author:** ![rmsmsgood](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rmsmsgood/32/20544_2.png) [@rmsmsgood](https://discourse.julialang.org/u/rmsmsgood)\
**Post date:** [January 1, 2024, 2:15am UTC](https://discourse.julialang.org/t/why-front-and-tail-are-defined-for-only-tuple/101961/12 "2024-01-01T02:15:47Z")

</div>

```julia
head1(x) = eval(Meta.parse("$(x)[$(":, " ^ (length(size(x))-1)) 1:(end-1)]"))
head2(x::AbstractArray{T,N}) where {T,N} = x[ntuple(i->Colon(),Val{N-1}())..., begin:end-1]

X = rand(5, 5, 5);

using BenchmarkTools

```

I have tested. Your implementation is x25 fatster than mine, thank you!

```julia
julia> @btime head1($X);
  1.783 ms (1065 allocations: 88.93 KiB)

julia> @btime head2($X);
  71.488 ns (1 allocation: 896 bytes)

```
