What evaluation mechanism is behind Mathematica support for multiple definitions of a function? What is the benefit of this?

I am studying the question What are the most common pitfalls awaiting new users?
Up to now, I have fulfilled some of the parts but yet don't understand which this problem is related to?
Consider the following code:

In my notebook, which is a test notebook to try out the codes in the above question, I have defined function f in 3 different places with the following sequence:

f[x_] := Expand[x^2](*SetDelayed*)
f[a + b]
(* a^2 + 2 a b + b^2 *)

f[x_] := Sqrt[x] /; x > 0(*Condition[patt,test]*)
f[2]
(* Sqrt[2] *)

f[-2]
(* f[-2] *)

f[x_?NumericQ] := NIntegrate[Sin[t^3], {t, 0, x}]
f[2]
(* Sqrt[2] *)


Getting the answer $\sqrt{2}$ for $f[2]$ clued that there exists a problem and function $f$ is not updated by its new definition. So I wrote this code:

?f

Globalf
f[x_]:=Sqrt[x]/;x>0

f[x_?NumericQ]:=NIntegrate[Sin[t^3],{t,0,x}]

f[x_]:=Expand[x^2]


And to see which of these definitions will be used each time the function $f$ is evoked, I wrote:

f[2]
(* Sqrt[2] *)

f[a + b]

(* a^2 + 2 a b + b^2 *)

f[6]
(* Sqrt[6] *)

f[-2]
(* 0.451948 *)


Now a few questions rise up:

1. which characteristic of the Wolfram language causes this (commands not having side effects, precedence of operators, .....,any thing else)?
2. What is the application of that characteristic in math that convinced the wolfram language developers to design the language this way?
3. Do we have to clear , Unset or UnsetDelay variables and functions each time we finish our work with them?
4. Is there a way to tell wolfram language explicitly which definition of $f$ should be used?
5. Is their something like local variable, private function in Wolfram language that enables us define variables just when we want to use them and better manage the memory (just like C++)?
• Mathematica is trying to use first the most specific definition availalbe. In particular when you type ?f you see Sqrt definition appears before NIntegrate that It does not always correlated with the order the definitions are introduced. So yes just add Clear[f] before each new definition. Sep 19, 2015 at 22:53
• As for (5), you can read about the scoping constructs available in Wolfram Language here. They perform garbage-collection. Sep 19, 2015 at 22:56
• C++ is probably the one of worst languages to use as a mental model when trying learn Mathematica semantics. Sep 19, 2015 at 23:46
• See The Ordering of Definitions "When you make a sequence of definitions in the Wolfram System, some may be more general than others. The Wolfram System follows the principle of trying to put more general definitions after more specific ones. This means that special cases of rules are typically tried before more general cases." Sep 20, 2015 at 5:28

1. Mathematica works by rewriting expressions. Each of your definitions involves a different condition triggering the rewrite.

2. Rewriting is the essence of symbolic mathematics. Mastery of this in Mathematica will allow you to implicitly delay evaluation until enough is known about the expression to evaluate it, efficiently evaluate special cases, terminate recursion cleanly, and much more.

3. If the conditions and patterns associated with a definition match a previous definition, you override that definition. Otherwise, you're adding a new definition which will apply in the defined circumstances.

4. Mathematica chooses the most specific definition that applies.

5. Mathematica has a number of scoping constructs including With, Module, and Block.

The use of pattern matching to inform Mathematica of which of several function definitions to apply to a function call is extremely powerful in practice. For one, it allows tail-recursive functions, functions which are essentially as efficient as iterators, to be defined. For two, it allows functions to exclude argument forms that are not acceptable, which can be used to make Mathematica, despite having untyped variables, more picky about arguments that C++.

Here is a simple example that defines a factorial function that only accepts positive integers and which is implemented using tail recursion.

g[1, val_] := val
g[k_, val_] := g[k - 1, k val]
fact[k_Integer /; k > 0] := g[k, 1]
fact[___] := \$Failed
`