Q:

why use def and val in Scala or vice versa

I know this has been discussed on SO in other posts before and I understand the basic difference between the use of def and val. def is used for defining a method and val for an immutable reference. What I am trying to accomplish by asking this question is to understand if there is something more to def. Can it be used interchangeably with a val?
Recently I tried the following code and cannot convince myself if my present understanding of def is sufficient:
scala> def i: Int = 3
i: Int

scala> i
res2: Int = 3

So I am curious, is this equivalent to val i = 3?
Then I tried this:
scala> i()
<console>:9: error: Int does not take parameters
i()

I did this just to test my understanding of the semantics of def. Now I want to know, when i is a method, why Scala complains with "...does not take parameters"?
Next I tried the following:
scala> def i(): Int = 3
i: ()Int

scala> i()
res4: Int = 3

This time Scala seems to agree that i is a method. So can I use def in place of val interchangeable to declare and initialize a variable?

A:

Both
def i = 3

and
def i() = 3

declare methods. The only difference is, that the first one is a method without a parameter list and the second is a method with an empty parameter list. The former is usually used for methods without side effects and the latter for methods with side effects.
You should use a val instead of a def if the value never changes and you want to avoid recomputing it. A def gets recomputed every time it is called, while a val is assigned a value only once.

A:

def defines a method, val defines an immutable value, as you already know.
One major difference is in when the expression on the right side of the = is evaluated. For a method, it is evaluated each time you call the method. For a value, it is evaluated when you initialize the value. See the difference:
scala> def i: Int = { println("Hello"); 3 }
i: Int

scala> i
Hello
res0: Int = 3

scala> i
Hello
res1: Int = 3

scala> val i: Int = { println("Hello"); 3 }
Hello
i: Int = 3

scala> i
res2: Int = 3

A:

Just to add on the top of the Kim's answer, you can override def by val.
// Entering paste mode (Ctrl+D to finish)

trait A {
  def i: Int
  def num: Long
}

class B extends A {
  val i = 7
  val num = 20L
}

// Exiting paste mode, now interpreting.

defined trait A
defined class B

scala> val b = new B
b: B = B@2d62bdd8

scala> b.i
res1: Int = 7

scala> b.num
res2: Long = 20

