Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save programaker/35e45ab807184d6edc25957d25cb8138 to your computer and use it in GitHub Desktop.
Save programaker/35e45ab807184d6edc25957d25cb8138 to your computer and use it in GitHub Desktop.
Classes as modules of partially-applied functions
//Functional programming is all about:
//. Pure, total and deterministic functions
//. ADTs (Algebraic Data Types)
//. Typeclasses
//However, in hybrid languages like Scala, OOP classes can still play a role!
//---------------------------------------------------------
//Given:
//A simple domain class
case class Frunfles(id: Long, value: String)
//A package object acting as a module containing stand-alone functions
//for CRUD operations on Frunfles
package app.frunfles
package object crud {
def findAllFrunfles[F[_]: Monad](db: DatabaseConnection): F[List[Frunfles]] = ???
def findFrunflesById[F[_]: Monad](db: DatabaseConnection, id: Long): F[Option[Frunfles]] = ???
def findFrunflesByValue[F[_]: Monad](db: DatabaseConnection, value: String): F[Option[Frunfles]] = ???
def insertFrunfles[F[_]: Monad](db: DatabaseConnection, f: Frunfles): F[Unit] = ???
def updateFrunfles[F[_]: Monad](db: DatabaseConnection, f: Frunfles): F[Unit] = ???
def deleteFrunfles[F[_]: Monad](db: DatabaseConnection, id: Long): F[Unit] = ???
}
//These functions are quite repetitive; all of then require `[F[_]: Monad]` and `db: DatabaseConnection`,
//and this is a tame example! Techniques like Tagless Final or Free Monads can make function signatures
//have many context bounds (like `: Monad` in the example) or implicit parameters.
//---------------------------------------------------------
//Classes to the rescue!
package app.frunfles.crud
final class FrunflesRepository[F[_]: Monad](db: DatabaseConnection) {
def findAllFrunfles(): F[List[Frunfles]] = ???
def findFrunflesById(id: Long): F[Option[Frunfles]] = ???
def findFrunflesByValue(value: String): F[Option[Frunfles]] = ???
def insertFrunfles(f: Frunfles): F[Unit] = ???
def updateFrunfles(f: Frunfles): F[Unit] = ???
def deleteFrunfles(id: Long): F[Unit] = ???
}
//The class can group all the common parts in its declaration and constructor!
//Its like in Math when we factor by taking a common term => ab + ac = a(b + c)
//Making the `DatabaseConnection` available to all functions and removing the parameter
//is similar to apply only the `db` parameter to produce new functions.
//That's why the class is a "module of partially-applied functions"!
//Conclusion: you can start you code with stand-alone functions and
//"factor" them with a class when necessary.
@programaker
Copy link
Author

Another discussion on the topic: https://twitter.com/alexelcu/status/1359034466572861441
A more succinct definition: Classes are closures with names

@programaker
Copy link
Author

Breaking news! Scala 3 context functions offer an alternative approach to classes and Reader: https://gist.github.com/programaker/51c31e8c1ca3d4715b872d610c1a636c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment