Skip to content

Instantly share code, notes, and snippets.

@vicaba
Created December 3, 2023 09:40
Show Gist options
  • Select an option

  • Save vicaba/cb01dce0c297f940e46d0f12bac6b032 to your computer and use it in GitHub Desktop.

Select an option

Save vicaba/cb01dce0c297f940e46d0f12bac6b032 to your computer and use it in GitHub Desktop.

Revisions

  1. vicaba created this gist Dec 3, 2023.
    65 changes: 65 additions & 0 deletions solid-lsp.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,65 @@
    case class User(name: String, email: String)
    val dummyUser = User("Dummy", "[email protected]")

    // Not using LSP
    object NotUsingLsp {
    trait UserRepository {
    def insert(user: User): Int
    def read(id: Int): User
    }

    class MysqlUserRepository extends UserRepository {
    def insert(user: User): Int = ???
    def read(id: Int): User = ???
    }

    class MongoDbUserRepository extends UserRepository {
    def insert(user: User): Int = ???
    def read(id: Int): User = throw new Exception("This Mongo doesn't support read")
    }

    class MyController(userRepository: UserRepository) {
    userRepository.read(1)
    userRepository.insert(dummyUser) // 1. Will the insert work?
    // 2. If the injected repository is a MysqlUserRepository: we are good!
    // 3. If the injected repository is a MongoDbUserRepository: exception.
    // 4. Same interface, but: sometimes it works, sometimes it doesn't...
    }

    }

    // Using LSP
    object UsingLsp {
    // 1. We can seggregating interfaces, so we have more flexibility in the composition
    trait WriteUserRepository {
    def insert(user: User): Int
    }

    trait ReadUserRepository {
    def read(id: Int): User
    }

    class MysqlUserRepository extends WriteUserRepository with ReadUserRepository {
    def insert(user: User): Int = ???
    def read(id: Int): User = ???
    }

    class MongoDbUserRepository extends WriteUserRepository {
    def insert(user: User): Int = ???
    }

    class MyController(
    readUserRepository: ReadUserRepository,
    writeUserRepository: WriteUserRepository
    ) {
    readUserRepository.read(1)
    writeUserRepository.insert(dummyUser)
    // 2. We are seggregating interfaces, so we have more flexibility in the composition
    }

    // 3. Or we can use intersection types! Here, the only valid implementation is Mysql
    class AnotherMyController(readAndWriteUserRepository: ReadUserRepository & WriteUserRepository) {
    readAndWriteUserRepository.read(1)
    readAndWriteUserRepository.insert(dummyUser)
    }
    }