Last active
December 11, 2015 23:39
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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