Skip to content

Commit

Permalink
refactored play-functional to make it clearer + moved Constraints bac…
Browse files Browse the repository at this point in the history
…k to play core project because I want to keep Forms as is for now
  • Loading branch information
mandubian committed Feb 19, 2013
1 parent 2fb3e96 commit 6e292ef
Show file tree
Hide file tree
Showing 11 changed files with 200 additions and 160 deletions.
16 changes: 8 additions & 8 deletions framework/project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ object PlayBuild extends Build {
)
).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*)

lazy val DataValidationProject = Project(
"Play-DataValidation",
file("src/play-datavalidation"),
lazy val DataCommonsProject = Project(
"Play-DataCommons",
file("src/play-datacommons"),
settings = buildSettingsWithMIMA ++ Seq(
previousArtifact := Some("play" % {"play-datavalidation_"+previousScalaVersion} % previousVersion),
libraryDependencies := dataValidationDependencies,
previousArtifact := Some("play" % {"play-datacommons"+previousScalaVersion} % previousVersion),
libraryDependencies := dataCommonsDependencies,
publishTo := Some(playRepository),
scalacOptions ++= Seq("-encoding", "UTF-8", "-Xlint","-deprecation", "-unchecked", "-feature"),
publishArtifact in packageDoc := buildWithDoc,
Expand All @@ -139,7 +139,7 @@ object PlayBuild extends Build {
publishArtifact in (Compile, packageSrc) := true
)
).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*)
.dependsOn(IterateesProject, FunctionalProject, DataValidationProject)
.dependsOn(IterateesProject, FunctionalProject, DataCommonsProject)

lazy val PlayExceptionsProject = Project(
"Play-Exceptions",
Expand Down Expand Up @@ -371,7 +371,7 @@ object PlayBuild extends Build {
TemplatesCompilerProject,
IterateesProject,
FunctionalProject,
DataValidationProject,
DataCommonsProject,
JsonProject,
RoutesCompilerProject,
PlayProject,
Expand Down Expand Up @@ -624,7 +624,7 @@ object PlayBuild extends Build {
"org.scala-lang" % "scala-reflect" % "2.10.0"
)

val dataValidationDependencies = Seq(
val dataCommonsDependencies = Seq(
"org.scala-lang" % "scala-reflect" % "2.10.0"
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package play.api.data.validation

/**
* A validation error.
*
* @param message the error message
* @param args the error message arguments
*/
case class ValidationError(message: String, args: Any*)

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package play.api.libs.functional

import scala.language.higherKinds

trait Alternative[M[_]]{

def app: Applicative[M]
def |[A,B >: A](alt1: M[A], alt2 : M[B]): M[B]
def empty: M[Nothing]
//def some[A](m: M[A]): M[List[A]]
//def many[A](m: M[A]): M[List[A]]

}

class AlternativeOps[M[_],A](alt1: M[A])(implicit a: Alternative[M]){

def |[B >: A](alt2: M[B]):M[B] = a.|(alt1,alt2)
def or[B >: A](alt2: M[B]):M[B] = |(alt2)

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package play.api.libs.functional

import scala.language.higherKinds

trait Applicative[M[_]]{

def pure[A](a: A): M[A]
def map[A,B](m: M[A], f: A => B): M[B]
def apply[A,B](mf: M[A => B], ma: M[A]): M[B]

}

This comment has been minimized.

Copy link
@mandubian

mandubian Oct 24, 2013

Author Contributor

I don't know any blog about it sorry...
You have to implement a Application[YourClass[_]]...
Have a look at Reads.scala file which implements Applicative[Reads]

Then as soon as you have an Applicative[YourClass], you can compose it using FunctionalBuilder exactly as we do with our Reads!

class ApplicativeOps[M[_],A](ma:M[A])(implicit a:Applicative[M]){

def ~>[B](mb: M[B]):M[B] = a(a(a.pure((_:A) => (b:B) => b), ma),mb)
def andKeep[B](mb: M[B]):M[B] = ~>(mb)

def <~[B](mb: M[B]):M[A] = a(a(a.pure((a:A) => (_:B) => a), ma),mb)
def keepAnd[B](mb: M[B]):M[A] = <~(mb)

def <~>[B,C](mb: M[B])(implicit witness: <:<[A,B => C]): M[C] = apply(mb)
def apply[B,C](mb: M[B])(implicit witness: <:<[A,B => C]): M[C] = a(a.map(ma,witness),mb)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package play.api.libs.functional

import scala.language.higherKinds

trait Variant[M[_]]

trait Functor[M[_]] extends Variant[M]{

def fmap[A,B](m: M[A], f: A => B): M[B]

}

trait InvariantFunctor[M[_]] extends Variant[M]{

def inmap[A,B](m: M[A], f1: A => B, f2: B => A): M[B]

}

trait ContravariantFunctor[M[_]] extends Variant[M]{

def contramap[A,B](m: M[A], f1: B => A): M[B]

}

class FunctorOps[M[_],A](ma: M[A])(implicit fu: Functor[M]){

def fmap[B](f: A => B):M[B] = fu.fmap(ma, f)

}

class ContravariantFunctorOps[M[_],A](ma:M[A])(implicit fu:ContravariantFunctor[M]){

def contramap[B](f: B => A):M[B] = fu.contramap(ma, f)

}

class InvariantFunctorOps[M[_],A](ma:M[A])(implicit fu:InvariantFunctor[M]){

def inmap[B](f: A => B, g: B => A):M[B] = fu.inmap(ma, f, g)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package play.api.libs.functional

trait Monoid[A] {

def append(a1: A, a2: A): A
def identity: A

}

/* A practical variant of monoid act/action/operator (search on wikipedia)
* - allows to take an element A to create a B
* - allows a prepend/append a A to a B
* cf Reducer[JsValue, JsArray]
*/
trait Reducer[A, B] {

def unit(a: A): B
def prepend(a: A, b: B): B
def append(b: B, a: A): B

}

object Reducer {
def apply[A, B](f: A => B)(implicit m: Monoid[B]) = new Reducer[A, B] {
def unit(a: A): B = f(a)
def prepend(a: A, b: B) = m.append(unit(a), b)
def append(b: B, a: A) = m.append(b, unit(a))
}
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,13 @@
package play.api.libs.functional

import scala.language.higherKinds
import scala.language.implicitConversions

class FunctorOps[M[_],A](ma: M[A])(implicit fu: Functor[M]){

def fmap[B](f: A => B):M[B] = fu.fmap(ma, f)

}

class ContravariantFunctorOps[M[_],A](ma:M[A])(implicit fu:ContravariantFunctor[M]){

def contramap[B](f: B => A):M[B] = fu.contramap(ma, f)

}

class InvariantFunctorOps[M[_],A](ma:M[A])(implicit fu:InvariantFunctor[M]){

def inmap[B](f: A => B, g: B => A):M[B] = fu.inmap(ma, f, g)

}

class ApplicativeOps[M[_],A](ma:M[A])(implicit a:Applicative[M]){
case class ~[A,B](_1:A,_2:B)

def ~>[B](mb: M[B]):M[B] = a(a(a.pure((_:A) => (b:B) => b), ma),mb)
def andKeep[B](mb: M[B]):M[B] = ~>(mb)
trait FunctionalCanBuild[M[_]]{

def <~[B](mb: M[B]):M[A] = a(a(a.pure((a:A) => (_:B) => a), ma),mb)
def keepAnd[B](mb: M[B]):M[A] = <~(mb)
def apply[A,B](ma: M[A], mb: M[B]): M[A ~ B]

def <~>[B,C](mb: M[B])(implicit witness: <:<[A,B => C]): M[C] = apply(mb)
def apply[B,C](mb: M[B])(implicit witness: <:<[A,B => C]): M[C] = a(a.map(ma,witness),mb)
}

class FunctionalBuilderOps[M[_],A](ma: M[A])(implicit fcb: FunctionalCanBuild[M]){
Expand All @@ -43,82 +20,6 @@ class FunctionalBuilderOps[M[_],A](ma: M[A])(implicit fcb: FunctionalCanBuild[M]
def and[B](mb: M[B]): FunctionalBuilder[M]#CanBuild2[A,B] = this.~(mb)
}

trait Applicative[M[_]]{

def pure[A](a: A): M[A]
def map[A,B](m: M[A], f: A => B): M[B]
def apply[A,B](mf: M[A => B], ma: M[A]): M[B]

}

class AlternativeOps[M[_],A](alt1: M[A])(implicit a: Alternative[M]){

def |[B >: A](alt2: M[B]):M[B] = a.|(alt1,alt2)
def or[B >: A](alt2: M[B]):M[B] = |(alt2)
}

trait Alternative[M[_]]{

def app: Applicative[M]
def |[A,B >: A](alt1: M[A], alt2 : M[B]): M[B]
def empty: M[Nothing]
//def some[A](m: M[A]): M[List[A]]
//def many[A](m: M[A]): M[List[A]]

}

trait FunctionalCanBuild[M[_]]{

def apply[A,B](ma: M[A], mb: M[B]): M[A ~ B]

}

trait Variant[M[_]]

trait Functor[M[_]] extends Variant[M]{

def fmap[A,B](m: M[A], f: A => B): M[B]

}

trait InvariantFunctor[M[_]] extends Variant[M]{

def inmap[A,B](m: M[A], f1: A => B, f2: B => A): M[B]

}

trait ContravariantFunctor[M[_]] extends Variant[M]{

def contramap[A,B](m: M[A], f1: B => A): M[B]

}

trait Monoid[A] {
def append(a1: A, a2: A): A
def identity: A
}

/* A practical variant of monoid act/action/operator (search on wikipedia)
* - allows to take an element A to create a B
* - allows a prepend/append a A to a B
* cf Reducer[JsValue, JsArray]
*/
trait Reducer[A, B] {
def unit(a: A): B
def prepend(a: A, b: B): B
def append(b: B, a: A): B
}

object Reducer {
def apply[A, B](f: A => B)(implicit m: Monoid[B]) = new Reducer[A, B] {
def unit(a: A): B = f(a)
def prepend(a: A, b: B) = m.append(unit(a), b)
def append(b: B, a: A) = m.append(b, unit(a))
}
}

case class ~[A,B](_1:A,_2:B)

class FunctionalBuilder[M[_]](canBuild: FunctionalCanBuild[M]){

class CanBuild2[A1, A2](m1: M[A1], m2: M[A2]){
Expand Down Expand Up @@ -804,48 +705,6 @@ class FunctionalBuilder[M[_]](canBuild: FunctionalCanBuild[M]){
}

}
package syntax {

object `package` {

implicit def toAlternativeOps[M[_],A](a: M[A])(implicit app: Alternative[M]): AlternativeOps[M,A] = new AlternativeOps(a)

implicit def toApplicativeOps[M[_],A](a: M[A])(implicit app: Applicative[M]): ApplicativeOps[M,A] = new ApplicativeOps(a)

implicit def toFunctionalBuilderOps[M[_],A](a: M[A])(implicit fcb: FunctionalCanBuild[M]) = new FunctionalBuilderOps[M,A](a)(fcb)

implicit def functionalCanBuildApplicative[M[_]](implicit app: Applicative[M]): FunctionalCanBuild[M] = new FunctionalCanBuild[M] {

def apply[A,B](a: M[A], b: M[B]): M[A~B] = app.apply(app.map[A, B => A ~ B](a, a => ((b: B) => new ~(a,b))),b)

}

implicit def functorOption: Functor[Option] = new Functor[Option] {

def fmap[A,B](a:Option[A], f: A => B):Option[B] = a.map(f)

}

implicit def applicativeOption: Applicative[Option] = new Applicative[Option]{

def pure[A](a: A):Option[A] = Some(a)

def map[A,B](m:Option[A], f: A => B):Option[B] = m.map(f)

def apply[A,B](mf:Option[A => B], ma: Option[A]):Option[B] = mf.flatMap(f => ma.map(f))

}

implicit def toFunctorOps[M[_], A](ma: M[A])(implicit fu: Functor[M]): FunctorOps[M, A] = new FunctorOps(ma)
implicit def toContraFunctorOps[M[_], A](ma: M[A])(implicit fu: ContravariantFunctor[M]): ContravariantFunctorOps[M, A] = new ContravariantFunctorOps(ma)
implicit def toInvariantFunctorOps[M[_], A](ma: M[A])(implicit fu: InvariantFunctor[M]): InvariantFunctorOps[M, A] = new InvariantFunctorOps(ma)

def unapply[B, A](f: B => Option[A]) = { b: B => f(b).get }

def unlift[A, B](f: A => Option[B]): A => B = Function.unlift(f)

}
}


/* the terrific scala template that generates scala
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package play.api.libs.functional.syntax

import scala.language.higherKinds
import scala.language.implicitConversions

import play.api.libs.functional._

/**
* Don't forget to {{{import play.api.libs.functional.syntax._}}} to enable functional combinators
* when using Json API.
*/
object `package` {

implicit def toAlternativeOps[M[_],A](a: M[A])(implicit app: Alternative[M]): AlternativeOps[M,A] = new AlternativeOps(a)

implicit def toApplicativeOps[M[_],A](a: M[A])(implicit app: Applicative[M]): ApplicativeOps[M,A] = new ApplicativeOps(a)

implicit def toFunctionalBuilderOps[M[_],A](a: M[A])(implicit fcb: FunctionalCanBuild[M]) = new FunctionalBuilderOps[M,A](a)(fcb)

implicit def functionalCanBuildApplicative[M[_]](implicit app: Applicative[M]): FunctionalCanBuild[M] = new FunctionalCanBuild[M] {

def apply[A,B](a: M[A], b: M[B]): M[A~B] = app.apply(app.map[A, B => A ~ B](a, a => ((b: B) => new ~(a,b))),b)

}

implicit def functorOption: Functor[Option] = new Functor[Option] {

def fmap[A,B](a:Option[A], f: A => B):Option[B] = a.map(f)

}

implicit def applicativeOption: Applicative[Option] = new Applicative[Option]{

def pure[A](a: A):Option[A] = Some(a)

def map[A,B](m:Option[A], f: A => B):Option[B] = m.map(f)

def apply[A,B](mf:Option[A => B], ma: Option[A]):Option[B] = mf.flatMap(f => ma.map(f))

}

implicit def toFunctorOps[M[_], A](ma: M[A])(implicit fu: Functor[M]): FunctorOps[M, A] = new FunctorOps(ma)
implicit def toContraFunctorOps[M[_], A](ma: M[A])(implicit fu: ContravariantFunctor[M]): ContravariantFunctorOps[M, A] = new ContravariantFunctorOps(ma)
implicit def toInvariantFunctorOps[M[_], A](ma: M[A])(implicit fu: InvariantFunctor[M]): InvariantFunctorOps[M, A] = new InvariantFunctorOps(ma)

def unapply[B, A](f: B => Option[A]) = { b: B => f(b).get }

def unlift[A, B](f: A => Option[B]): A => B = Function.unlift(f)

}

Loading

0 comments on commit 6e292ef

Please sign in to comment.