Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Data Modeling in FP Vs. OOP

DZone's Guide to

Data Modeling in FP Vs. OOP

Is object-oriented programming really all that useful? Check out this post on one developer's thoughts on functional programming and OOP.

· Java Zone ·
Free Resource

Verify, standardize, and correct the Big 4 + more– name, email, phone and global addresses – try our Data Quality APIs now at Melissa Developer Portal!

At Scalapeño this year, I argued in my keynote speech that the best and most unique parts of Scala are the ones suited to functional programming, including traits (when used for type classes, modules, and sum types), case classes (when used for product types), higher-kinded types, dot method syntax (which is useful for method chaining and namespacing), and various features of Scala’s type system.

In the Twitter aftermath that followed, I further argued that object-oriented programming — by which I mean inheritance hierarchies (typified by the Scala collections inheritance hierarchy) and subtyping (beyond its use for modeling sum types, modules, and type classes) — isn’t useful. That is, it doesn’t have any real world practical benefits over functional programming.

Some claimed without supporting evidence that Scala’s embrace of object-oriented programming helps working programmers solve real problems. To settle the issue once and for all (at least for some people), I issued the following public challenge:

In a world of millions of object-oriented programmers, I received a grand total of 3 responses (!).

In this blog post, I’ll look at one of the responses and show how you can solve the same problem using standard techniques from functional programming. Then, every reader can decide for themselves whether OOP adds anything of value to what I have defined as the good parts of the Scala programming language.

Data Modeling

The first response to my challenge was a problem in data modeling by Christopher Hunt, a veteran of Lightbend:

sealed abstract class Event(val id: Int)

final case class CountersUpdated(
  override val id: Int,
  counter: Option[Int]) extends Event(id)

final case class DeviceIdUpdated(
  override val id: Int,
  deviceId: Int) extends Event(id)


This example uses inheritance, not for type classes, modules, or sum type emulation, but to provide the following benefits:

  1. The compile-time guarantee that every event has an id of type Int, which means the same thing across every event.
  2. The flexibility for each event to have its own custom payload (for example, one has a counter: Option[Int], and another has a deviceId: Int).

Let’s take a look at how functional programming can provide us with these same benefits, without the use of inheritance.

Naive Solution

In functional programming, we might choose to naively model this problem with a single sum type:

data Event
  = CountersUpdated Int (Maybe Int)
  | DeviceIdUpdated Int Int
// In Scala 2.x:
sealed trait Event
case class CountersUpdated(
  id: Int, counter: Option[Int]) extends Event
case class DeviceIdUpdated(
  id: Int, deviceId: Int) extends Event

// In Scala 3.x:
enum Event {
  case CountersUpdated(id: Int, counter: Option[Int])
  case DeviceIdUpdated(id: Int, deviceId: Int)
}


While this approach provides us with the ability to vary the payload for each type of event, we don’t have the compile time guarantee that each type of event has an event id.

We could regain this guarantee with classy lenses, but we don’t need anything as fancy as that: we just need to take advantage of the full power of algebraic data types to remove the duplication in our current data model.

Idiomatic Solution

In any case, where we have a sum type, whose terms share a common piece of data, we can apply a refactoring: we can extract a new product type with the common data and push the sum type deeper as another term of that product.

In this case, our event id is the common piece of data, so we can extract a product type that includes both the id and a new sum type that captures differences between different events:

data Event = Event Int Payload
data Payload
  = CountersUpdated (Maybe Int)
  | DeviceIdUpdated Int
// In Scala 2.x:
final case class Event(id: Int, payload: Payload)
sealed trait Payload
case class CountersUpdated(counter: Option[Int]) extends Payload
case class DeviceIdUpdated(deviceId: Int) extends Payload

// In Scala 3.x:
final case class Event(id: Int, payload: Payload)
enum Payload {
  case CountersUpdated(counter: Option[Int])
  case DeviceIdUpdated(deviceId: Int)
}


This demonstrates simple refactoring. Now, let's us not just vary the payload depending on the type of event, but let's have a compile-time guarantee that all events share common data.

Compared with the object-oriented solution, the new data model is the same size (smaller in Scala 3.x!), has all the benefits of the object-oriented solution and does not conflate abstraction (the capability to extract an id from a type) with the data model.

The new data model is squeaky clean and contains no logic, which is better reserved for type classes because it’s far more flexible and precisely models the problem domain!

While developers can argue which approach they prefer, it’s clear the object-oriented solution has no benefits over the functional programming solution.

Summary

In this post, I showed how functional programming compares to object-oriented programming. Rather than use a contrived example that might skew the results in favor of functional programming, I took my example from proponents of OOP.

Stay tuned for other posts in this series, where I look at other challenges problems. And, if you have some small, self-contained example and wonder how to do the same thing in functional programming, please post it in the comments below!

Data Modeling in FP vs OOP was published on by .


You Might Also Enjoy

Developers! Quickly and easily gain access to the tools and information you need! Explore, test and combine our data quality APIs at Melissa Developer Portal – home to tools that save time and boost revenue. 

Topics:
java ,data modeling ,oop ,object oriented programming ,scala ,functional programming

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}