Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

A macro library that converts native imperative syntax to scalaz's monadic expressions

License

NotificationsYou must be signed in to change notification settings

scala-steward/each

 
 

Join the chat at https://gitter.im/ThoughtWorksInc/eachBuild StatusLatest version

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.

Motivation

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))

Usage

Step 1: Add the following line in your build.sbt

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.

Step 2: In your source file, importmonadic andeach method

importcom.thoughtworks.each.Monadic._

Step 3: Import implicit Monad instances

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.

Step 4: Usemonadic[F] to create a monadic expression

importcom.thoughtworks.each.Monadic._importscalaz.std.option._valresult:Option[String]= monadic[Option] {"Hello, Each!"}

Step 5: In themonadic block, use.each postfix to extract each element in aF

importcom.thoughtworks.each.Monadic._importscalaz.std.option._valname=Option("Each")valresult:Option[String]= monadic[Option] {"Hello,"+ name.each+"!"}

Exception handling

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.

for loop

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.

for comprehension

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.

Limitation

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.

Links

About

A macro library that converts native imperative syntax to scalaz's monadic expressions

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Scala98.4%
  • Logos1.6%

[8]ページ先頭

©2009-2025 Movatter.jp