Skip to content

Instantly share code, notes, and snippets.

@andypetrella
Last active December 11, 2015 23:39
Show Gist options
  • Save andypetrella/4678477 to your computer and use it in GitHub Desktop.
Save andypetrella/4678477 to your computer and use it in GitHub Desktop.
Another try to define Model with Id, crud and so on. But using Type Classes rather than self type and recursive type definition
package tc
object Main extends App {
import tc.Action._
import tc.Generator._
import tc.User._
// a user
val user = User(None, "noootsab", true, 31)
println(s"User $user")
//it's implicitly defined crud implementation
val userCrud = implicitly[CRUD[User]]
println(s"CRUD $userCrud")
//get a user of id 1
val getUserAction = userCrud.get(1)
println(s"Get Action: $getUserAction")
val insertUserAction = userCrud.insert(user)
println(s"Insert Action: $insertUserAction")
// Dummy connection... to have things compiling ^^
object FakeConnection extends Connection
val insertResult = insertUserAction(FakeConnection)
println(s"Insert: $insertResult")
val getResult = getUserAction(FakeConnection)
println(s"Get: $getResult")
}
package tc
/**
*
* A new try for Model definition using Type Classes and implicit context
* (could be even less verbose using macro (for ApplyId implementation for instance!))
*
*/
/**
* Can generate a new Id of type I (not functional style because `generate` is an operator)
*/
trait Generator[I] {
def generate:I
}
object Generator {
/**
* Sample impl for Int
*/
implicit object IntGenerator extends Generator[Int] {
def generate = scala.util.Random.nextInt
}
/**
* Useful generator that returns the same value all the time
* > when using like this, f.i., {updateWithId(a)(ConstantGenerator(i))}
*/
case class ConstantGenerator[I](i:I) extends Generator[I] {
def generate = i
}
}
/**
* A model is defining a simple contract :
* instances with an optional id of inner type I
*/
trait Model {
type I
def id:Option[I]
}
/**
* Useful type class that abstracts the generation of an id using the provided generator
*/
trait ApplyId[A<:Model] {
def updateWithId(a:A)(implicit g:Generator[A#I]):A
}
/**
* to have thing compiling...
*/
trait Connection
/**
* An abstraction over a computation occurring in the outside world (like a database or a rest service)
*/
trait Action[A] {
def apply(implicit c:Connection):A
// should define map, flatMap and WithFilter instance
// Because it's not so far from IO Monad and I think it should be a monad (didn't try the laws...)
// > this will enable to compose the actions into one; like a transaction
// >> add applicative builder, and we could also record rollbacks ^^
}
/**
* Some convenient helpers
*/
object Action {
def action[A](a: => A):Action[A] = new Action[A] {
def apply(implicit c:Connection):A = a
}
def action[A](f:Connection => A):Action[A] = new Action[A] {
def apply(implicit c:Connection):A = f(c)
}
}
/**
* Type classes for CRUD operations
*/
trait CRUD[A<:Model] extends Create[A] with Read[A] with Update[A] with Delete[A]
trait Create[A<:Model] {
def insert(a:A)(implicit w:ApplyId[A]):Action[A]
}
trait Read[A<:Model] {
def get(i:A#I):Action[Option[A]]
}
trait Update[A<:Model] {
def udpate(a:A):Action[A]
}
trait Delete[A<:Model] {
def remove(a:A):Action[Boolean]
}
package tc
/**
* Sample use case : User
*/
case class User(id:Option[Int], name:String, male:Boolean, age:Int) extends Model {
type I = Int
}
object User {
/**
* Implements the update with id strategy => put in a type class won't pollute the model definition
*/
implicit object applyId extends ApplyId[User] {
def updateWithId(u:User)(implicit g:Generator[Int]):User = u.copy(id = Some(g.generate))
}
/**
* Dummy implementation of crud operations for User
*/
implicit object UserCRUD extends CRUD[User] {
import Action._
def insert(a:User)(implicit w:ApplyId[User]) = action { w.updateWithId(a) }
def get(i:Int) = action { connection =>
println(s"We could use $connection within this action definition")
None
}
def udpate(a:User) = action { a }
def remove(a:User) = action { true }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment