# Why does Mathematica use [[ ]] notation for array indexing?

I am confused by why Mathematica uses [] to get the 3rd element, or [[i,j] to get the i,j-th element of a 2D array.

This seems counter-intuitive. Is this main reason for this to separate array-indexing from function calls?

i.e. where I see:

f[3, 4] <-- this is currently function call
f[[3, 4]] <-- f is a 2D array, and we're accessing 3,4-th element


Question:

Given that Mathematica knows the type of the arguments, can't it infer:

• if we're dealing with a function, apply arguments
• if the object is an array, index it?

Thus, why do we need separate [[ ]] notation for array indexing?

• most programs are written to also be read by other humans, in which case the distinction between [[ ]] and [ ] is helpful.
– rm -rf
Jul 10 '12 at 4:04

In addition to Brett's counter-example, it might be helpful to view this from Mathematica's philosophy, which is "everything is an expression". In this framework, you're not really indexing a 1D/2D array, but you're extracting a Part from an expression.

Indeed, you can use the ⟦ ⟧ notation on any expression, not just lists/matrices. For example:

Sin[x + y][]
(* x + y *)

Graphics[{Red, Disk[]}][[1, 1]]
(* RGBColor[1, 0, 0] *)


The output of Part on any expression can be viewed as the argument of some function in the FullForm of the expression. For example, breaking down the second example above:

Graphics[{Red, Disk[]}][]
(* {RGBColor[1, 0, 0], Disk[{0, 0}]} <-- argument of Graphics[] *)

Graphics[{Red, Disk[]}][[1, 1]]
(* RGBColor[1, 0, 0] <-- argument of List[] *)

Graphics[{Red, Disk[]}][[1, 2, 1]]
(* {0, 0} <-- argument of Disk[] *)


The 0th part is the Head of the entire expression. Since none of the above constitute as being a function call, it makes sense to use a different notation to avoid any ambiguity.

No.

For example, functions do not have to be atomic. It can be possible to extract parts from them (although it's generally not recommended.)

In:= if=Interpolation[Range^2]

Out= InterpolatingFunction[{{1,10}},<>]

In:= if

Out= 9

In:= if[]

Out= {{1,2,3,4,5,6,7,8,9,10}}


Let me give two examples illustrating the conceptual difference between Part and [].

Here's an example showing how parts of every expression, even graphics expressions, can be accessed using Part:

g = Graphics[Circle[]];
g
g // FullForm


so g is, in fact, Graphics[Circle[List[0, 0]]], even though Mathematica displays it in the front-end as So you can extract its parts directly: g[[1, 1, 2]] evaluates to 0, for example.

So Part is a way of accessing specific parts on an expression tree.

On the other hand, you can think of f as analogous to a hashtable. Thus,

Do[f[i] = RandomReal[], {i, 3}]
DownValues[f]
(*
{HoldPattern[f] :> 0.201718,
HoldPattern[f] :> 0.287401,
HoldPattern[f] :> 0.531829}
*)


and eg f evaluates to 0.287401. Defining this kind of thing with patterns, like f[x_]:=x^2 is no different: you are merely matching a more general pattern than you are with eg f. You could have arbitrary expressions inside the brackets, and this is often used to implement memoization, although it needs some work for more complicated situations.

So actually f[] and f are conceptually completely different operations.