- Notifications
You must be signed in to change notification settings - Fork0
A macro library that converts native imperative syntax to scalaz's monadic expressions
License
scala-steward/each
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
ThoughtWorks Each is a macro library that converts native imperative syntax toScalaz's monadic expression. See theobject cats inDsl.scala for the similar feature forCats.
There is a macro libraryStateless Future that providesawait
for asynchronous programming.await
is a mechanism that transform synchronous-like code into asynchronous expressions. C# 5.0, ECMAScript 7 and Python 3.5 also support the mechanism.
Theawait
mechanism in Stateless Future is implemented by an algorithm calledCPS transform. When learningscalaz, we found that the same algorithm could be applied for any monadic expression, includingOption
monad,IO
monad, andFuture
monad. So we started this project, Each.
Each is a superset ofawait
syntax. Each supports multiple types of monads, whileawait
only works withFuture
. When we perform a CPS transform for monadic expression with theFuture
monad, the use case looks almost the same as theawait
syntax inStateless Future. Each is like F#'sComputation Expressions, except Each reuses the normal Scala syntax instead of reinventing new syntax.
For example:
importcom.thoughtworks.each.Monadic._importscalaz.std.scalaFuture._// Returns a Future of the sum of the length of each string in each parameter Future,// without blocking any thread.defconcat(future1:Future[String],future2:Future[String]):Future[Int]= monadic[Future] { future1.each.length+ future2.each.length}
The similar code works for monads other thanFuture
:
importcom.thoughtworks.each.Monadic._importscalaz.std.option._defplusOne(intOption:Option[Int])= monadic[Option] { intOption.each+1}assert(plusOne(None)==None)assert(plusOne(Some(15))==Some(16))
importcom.thoughtworks.each.Monadic._importscalaz.std.list._defplusOne(intSeq:List[Int])= monadic[List] { intSeq.each+1}assert(plusOne(Nil)==Nil)assert(plusOne(List(15))==List(16))assert(plusOne(List(15,-2,9))==List(16,-1,10))
libraryDependencies+="com.thoughtworks.each"%%"each"%"latest.release"addCompilerPlugin("org.scalamacros"%"paradise"%"2.1.0" crossCrossVersion.full)
or%%%
for Scala.js projects:
libraryDependencies+="com.thoughtworks.each"%%%"each"%"latest.release"addCompilerPlugin("org.scalamacros"%"paradise"%"2.1.0" crossCrossVersion.full)
Note that ThoughtWorks Each requires Scalaz 7.2.x and does not compatible with Scala 7.1.x .
Seehttps://repo1.maven.org/maven2/com/thoughtworks/each/ for a list of available versions.
importcom.thoughtworks.each.Monadic._
Scalaz has providedOption
monad, so you just import it.
importcom.thoughtworks.each.Monadic._importscalaz.std.option._
Please import other monad instances if you need other monads.
importcom.thoughtworks.each.Monadic._importscalaz.std.option._valresult:Option[String]= monadic[Option] {"Hello, Each!"}
importcom.thoughtworks.each.Monadic._importscalaz.std.option._valname=Option("Each")valresult:Option[String]= monadic[Option] {"Hello,"+ name.each+"!"}
monadic
blocks do not supporttry
,catch
andfinally
. If you want these expressions, usethrowableMonadic
orcatchIoMonadic
instead, for example:
varcount=0valio= catchIoMonadic[IO] { count+=1// Evaluates immediatelyval_=IO(()).each// Pauses until io.unsafePerformIO()try { count+=1 (null:Array[Int])(0)// Throws a NullPointerException }catch {casee:NullPointerException=> { count+=1100 } }finally { count+=1 }}assertEquals(1, count)assertEquals(100, io.unsafePerformIO())assertEquals(4, count)
Note thatcatchIoMonadic
requires an implicit parameterscalaz.effect.MonadCatchIO[F]
instead ofMonad[F]
.scalaz.effect.MonadCatchIO[F]
is only provided forscalaz.effect.IO
by default.
Each supports.each
magic in afor
loop on any instances that supportFoldable
type class. For example, you couldimport scalaz.std.list._
to enable theFoldable
type class forList
.
importcom.thoughtworks.each.Monadic._importscalaz.std.list._importscalaz.std.option._valn=Some(10)@monadic[Option]valresult= {varcount=1for (i<-List(300,20)) { count+= i* n.each } count}Assert.assertEquals(Some(3201), result)
Note that you need to use@monadic[Option]
annotation instead ofmonadic[Option]
block to in order to enable thefor
loop syntax.
Each also supports.each
magic in afor
comprehension on any instances that supportTraverse
andMonadPlus
type class.
importcom.thoughtworks.each.Monadic._importscalaz.std.list._valn=Some(4000)@monadic[Option]valresult= {for { i<-List(300,20) (j, k)<-List(50000->"1111",600000->"yyy")if i> n.each-3900 a= i+ j }yield { a+ n.each* k.length }}Assert.assertEquals(Some(List(66300,612300)), result)
Note that you need to use@monadic[Option]
annotation instead ofmonadic[Option]
block to in order to enable thefor
comprehension syntax.
If acall-by-name parameter of a method call is a monadic expression,Each
will transform the monadic expression before the method call. The behavior was discussed at#37.
definnerFailureFuture=Future.failed(newException("foo"))valsomeValue=Some("value")valresult= monadic[Future] { someValue.getOrElse(innerFailureFuture.each)}
result
will be a future of failure because the above example equals to
definnerFailureFuture=Future.failed(newException("foo"))valsomeValue=Some("value")valresult= innerFailureFuture.map(someValue.getOrElse)
innerFailureFuture.each
is evaluated before being passed togetOrElse
method call, even ifgetOrElse
accepts a call-by-name parameter.
About
A macro library that converts native imperative syntax to scalaz's monadic expressions
Resources
License
Code of conduct
Security policy
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Languages
- Scala98.4%
- Logos1.6%