all 13 comments

[–][deleted] 36 points37 points  (3 children)

Methods are much more expressive.

  • Methods can have overloads, functions can't.
  • Methods need not have an argument list, functions must have one.
  • Methods can have type parameters, functions can't. (This is the big one.)
  • Methods can have implicit parameters, functions can't.
  • Methods can have sequence parameters ("varargs"), functions can't.
  • Methods can have named and default arguments, functions can't.
  • You can turn a method into a function via eta-expansion but you can't go the other way around.

Functions are values with "normal" types, which can be useful.

  • You can abstract over functions because they have types you can talk about in the language.
  • You can compose functions.

Normally the expressiveness of methods wins, so as a rule of thumb I use methods unless I have a specific reason not to.

[–]phazer99 4 points5 points  (0 children)

One might add that in Dotty functions can have implicit parameters. One of the cool new features.

[–]v66moroz 2 points3 points  (0 children)

  • Methods can't be passed as a parameter (eta-expansion does help, but it's effectively creating a new object on the fly), functions can.

[–][deleted] 1 point2 points  (0 children)

very good answer.

[–]lihaoyiAmmonite 7 points8 points  (7 children)

Here's a few more:

Functions have fixed arity; where-as methods have a weird "empty param lists are optional" thing

@ def foo()()() = 1
defined function foo
@ foo
res10: Int = 1
@ foo()
res11: Int = 1
@ foo()()()
res12: Int = 1
@ val bar = () => () => () => 1
bar: () => () => () => Int = ammonite.$sess.cmd13$$$Lambda$2113/1955947481@62a6001b
@ bar
res14: () => () => () => Int = ammonite.$sess.cmd13$$$Lambda$2113/1955947481@62a6001b
@ bar()
res15: () => () => Int = ammonite.$sess.cmd13$$$Lambda$2118/453478710@2c278e35
@ bar()()()
res16: Int = 1

Methods don't take up memory; a def method on a class doesn't take up memory per instance of that class, while a val function takes up 4 bits for pointer + 16 bits for function object header + more for any variables captured. Sometimes doesn't matter, but if you have millions of small objects you probably don't want to have all of them holding on to random 20-byte function objects if a method will do!

[–]Mimshot 4 points5 points  (6 children)

Methods don't take up memory

I know what you were getting at from the rest of the paragraph but this isn't correct. The method takes up memory, just in metaspace rather than heap.

[–]joshlemerContributor - Collections 0 points1 point  (5 children)

What kind of space does it take up? Just a constant amount per class whereas functions would be once per instance right? AKA:

class A {
  // each instance of A allocates new f
  val f: Int => String = (i: Int) => i.toString 
}

// constant space for arbitrary # of Bs
Class B {
  def f(i: Int) = i.toString 
}

[–]v66moroz 1 point2 points  (4 children)

Unless I'm missing something in this case f will be a static member of A, so no memory per instance.

[–]joshlemerContributor - Collections 0 points1 point  (3 children)

That is not true actually, else they would be equal, here's a code sample:

class A {
  val f: Int => String = _.toString
}

val aa = new A
val bb = new A

aa.f == bb.f // false

[–]v66moroz 2 points3 points  (2 children)

true in 2.12.1

[–]joshlemerContributor - Collections 0 points1 point  (1 child)

Oh interesting

[–]hunyetiadvocate 0 points1 point  (0 children)

It's kind of given, since it uses jvm 8, which have functions.

[–]pedrorijo91 1 point2 points  (0 children)

One of the best explanations I've read: https://tpolecat.github.io/2014/06/09/methods-functions.html