0

Say I have a case class like this:

final case class Foo(a : String)

and I want to have a method that operates on Foo. Then I can do:

final case class Foo(a : String){

   def m : Int = a.toInt

}

or I could do


object Foo{

   def m (f : Foo) : Int = f.a.toInt

}

Which one is best or what are advantages and disadvantages of each?

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
Mojo
  • 1,152
  • 1
  • 8
  • 16

3 Answers3

2

Actually you're asking what is the difference between x.m() and m(x). The difference is how method is resolved. There can be different methods with the same name m.

In the case x.m() method is resolved dynamically, at runtime (overriding, late binding, subtype polymorphism)

class Foo {
  def m(): Unit = println("Foo")
}
class Bar extends Foo {
  override def m(): Unit = println("Bar")
}

val x: Foo = new Bar
x.m() // Bar

In the case m(x) method is resolved statically, at compile time (overloading, early binding, ad hoc polymorphism)

class Foo
class Bar extends Foo

def m(x: Foo): Unit = println("Foo")
def m(x: Bar): Unit = println("Bar")

val x: Foo = new Bar
m(x) // Foo

One of classes Foo, Bar can be a case class.

The latter approach ("statical") can be implemented also with type classes rather than overloading

trait DoM[T] {
  def m(t: T): Unit
}
def m[T](t: T)(implicit dm: DoM[T]): Unit = dm.m(t)

class Foo 
class Bar extends Foo

implicit val fooDoesM: DoM[Foo] = _ => println("Foo")
implicit val barDoesM: DoM[Bar] = _ => println("Bar")

val x: Foo = new Bar
m(x) // Foo
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
0

Conceptually at least, a method on a class is usually an operation that transforms an instance.

You can picture the lifecycle of an object like this:

A -----> MyType -----> Mytype ----> B

  \___/         \___/        \___/  
    |             |            |
construct    transform   eliminate/consume/fold

It is customary to put constructors in the companion object and transformations in the class definition.

In Scala 3 I found the following pattern useful:

  • Put core / small methods in the class itself
  • Put bigger / less common methods in an extension section.
Juanpa
  • 667
  • 6
  • 8
-1

These two are meant for very different use-cases, so there should not be any comparision between them.

Anything which can be thought of as the "behaviour" of an object instance and is kind of intrinsic to the object is better to be implemented as class / case class methods.

This point is also related to shared behaviours and inheritance. It will be very cumbersome to manage behaviour overrides in inheritance hierarchies using companion objects. This will be pretty much impossible (and very ugly) to manage for anything which not sealed.

It will also allow you to easily refactor your code, in-case you need to abstract out this case class as a trait in future.

Anything which feels like an utility on top of the object instance can go to companion object, factory manager object or some util object.

sarveshseri
  • 13,738
  • 28
  • 47
  • 1
    *"Anything which can be thought of as the "behaviour" of an object instance and is kind of intrinsic to the object is better to be implemented as class / case class methods."* This is typical for OOP paradigm. But for FP paradigm on contrary it's more typical to describe a behavior via type classes rather than instance methods i.e. via `def m(f: Foo)` or `trait DoM[T] { def m(f: T) }` `implicit val fooDoM: DoM[Foo] = ...` – Dmytro Mitin Sep 21 '21 at 22:20
  • object oriented paradigm is neither imperitive nor functional. – sarveshseri Sep 21 '21 at 22:51
  • 1
    *"object oriented paradigm is neither imperitive nor functional."* Right. So? – Dmytro Mitin Sep 21 '21 at 22:56
  • The question is "What is the benefit of putting a method inside a case class compared to in its companion object?". And I have provided some context to some of those "situational" benefits. The question seems to be pretty much from the OOP's point of view. OP did not mention anything about functional paradigm at all. And diciplined use OOP can actually empower traditional FP code style. Also, `cats` typeclasses are also implemented as an OO hierarchy, it does not make them any less functional ? – sarveshseri Sep 21 '21 at 23:08
  • 1
    *"The question seems to be pretty much from the OOP's point of view."* Maybe yes, maybe no :) *"OP did not mention anything about functional paradigm at all"* Why should he? Scala is a multiparadigmatic language (a FP language). In some sense `def m(f: Foo): Int` looks like FP approach (from the world where things are resolved statically rather than dynamically). My point was that *"Anything which can be thought of as the "behaviour" of an object instance and is kind of intrinsic to the object is better to be implemented as class / case class methods"* is too categorical. – Dmytro Mitin Sep 21 '21 at 23:27
  • 1
    *"Also, cats typeclasses are also implemented as an OO hierarchy, it does not make them any less functional?"* They are implemented as OO hierarchies but their methods are not instance methods of type `T` (for a type class `TC[T]`). – Dmytro Mitin Sep 21 '21 at 23:30
  • @DmytroMitin When somebody asks "what are the benefits of doing A instead of B ?", then I think they want to know about benefits of doing A instead of B; and not to know about other things like X, Y, Z. Also, I am not sure what are we even discussing here. From your first comment, I thought that you were drawing a fine line between OOP and FP. But, in your second comment, you agreed to my statement but seemed to be asking a question. And I replied to that. Now, you are talking about cats implementing type classes, which is true. But what is the objective here ? What are we discussing ? – sarveshseri Sep 22 '21 at 08:23
  • 1
    *"What are we discussing?"* That your *"**Anything** which can be thought of as the "behaviour" of an object instance and is kind of intrinsic to the object is **better** to be implemented as class / case class methods"* is too categorical. There is also FP approach with type classes. And in some sense `def m(f : Foo)...` looks similar to the latter. – Dmytro Mitin Sep 22 '21 at 08:36
  • *"When somebody asks "what are the benefits of doing A instead of B ?", then I think they want to know about benefits of doing A instead of B; and not to know about other things like X, Y, Z."* It's disputable. "Benefits" are mentioned in the title. In the question itself it's "advantages and disadvantages of each". There are simply two ways of doing things (A and B), of describing abstract behavior, namely more OOP-ish way (dynamical, subtype polymorphism, overriding) and more FP-ish way (statical, type classes, ad hoc polymorphism, overloading etc.). – Dmytro Mitin Sep 22 '21 at 08:47
  • *"But, in your second comment, you agreed to my statement but seemed to be asking a question."* I agreed with "object oriented paradigm is neither imperitive nor functional", not with "**Anything** ... **better** ...". – Dmytro Mitin Sep 22 '21 at 08:55
  • While all of this is true, I think we are needlessly expanding the scope of a very simple _"behaviour"_ vs _"utility"_ question. Now, the implementation of this behaviour / utility can be by _class methods + object methods_ or _ad-hoc-polymorphism + implicit ops_ (both are good implementation choices and are subjected to other situational parameters). I just focused on the _class methods + object methods_, as that was what OP had in their question. And yes, I meant that you agreed with _"object oriented paradigm is neither imperitive nor functional"_. – sarveshseri Sep 22 '21 at 09:03
  • Well, in your answer you do not say that "both are good implementation choices", you say that the one is better than the other. – Dmytro Mitin Sep 22 '21 at 09:18
  • No. I never talked about inheritance vs ad-hoc-polymorphism. Since the question was only about class methods vs object methods, I said that "for anything intrinsic to the class, class methods are better (and I explicitly added that this is also related to shared behaviours and inheritance with a benfit of class methods in inheritance hierarchies). For anything which feels like an utility on top of object instance, companion object, factory or utility object methods are better". – sarveshseri Sep 22 '21 at 10:26