Skip to content

Commit

Permalink
Merge pull request #37 from jsuereth/wip/features-and-fixes-for-2.0
Browse files Browse the repository at this point in the history
Wip/features and fixes for 2.0
  • Loading branch information
jsuereth committed Mar 14, 2015
2 parents d7b1bdc + 3947d5f commit d5c8446
Show file tree
Hide file tree
Showing 19 changed files with 303 additions and 286 deletions.
29 changes: 29 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Use Docker-based container (instead of OpenVZ)
sudo: false

cache:
directories:
- $HOME/.ivy2/cache
- $HOME/.sbt/boot

language: scala

# TODO - we'd like to actually test everything, but the process library has a deadlock right now
jdk:
- openjdk6
- oraclejdk7

env:
matrix:
- SCRIPTED_TEST="test"

notifications:
email:
- joshua.suereth@typesafe.com

script:
- sbt -J-XX:ReservedCodeCacheSize=128m "$SCRIPTED_TEST"

# Tricks to avoid unnecessary cache updates
- find $HOME/.sbt -name "*.lock" | xargs rm
- find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm
18 changes: 1 addition & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ In Maven:

## Examples

Scala-arm provides many ways of managing resources and re-using code. Here's a few examples.
Scala-arm provides a way of managing resources and re-using code. Here's an example:

### Imperative Style

Expand All @@ -42,22 +42,6 @@ Scala-arm provides many ways of managing resources and re-using code. Here's a
read()
}

### Delimited continuation style

import resource._
import java.io.{File, FileInputStream => Fin, FileOutputStream => Fout}
def copyFile(from: File, to: File): Unit =
withResources {
val f = managed(new Fin(from)).reflect[Unit]
val t = managed(new Fout(to)).reflect[Unit]
val buffer = new Array[Byte](512)
def read(): Unit = f.read(buffer) match {
case -1 => ()
case n => t.write(buffer,0,n); read()
}
read()
}

For more information on usage, see [Usage](http://jsuereth.com/scala-arm/usage.html)

## SCALA LICENSE
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=0.13.1
sbt.version=0.13.7
16 changes: 2 additions & 14 deletions project/build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ object ArmDef extends Build {
crossScalaVersions := Seq("2.9.3", "2.10.4", "2.11.0"),
resolvers += "java.net repo" at "http://download.java.net/maven/2/",
libraryDependencies ++= dependencies,
autoCompilerPlugins := true,
addContinuations,
scalacOptions += "-P:continuations:enable"
scalacOptions += "-deprecation",
scalacOptions += "-feature"
) settings(releaseSettings:_*) settings(sonatypeSettings:_*) settings(publishSettings:_*) settings(websiteSettings:_*)) settings(bcSettings:_*)

def bcSettings: Seq[Setting[_]] = mimaDefaultSettings ++ Seq(
Expand Down Expand Up @@ -98,17 +97,6 @@ object ArmDef extends Build {
)
)

def addContinuations = libraryDependencies ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
// if scala 2.11+ is used, add dependency on scala-xml module
case Some((2, scalaMajor)) if scalaMajor >= 11 =>
Seq(compilerPlugin("org.scala-lang.plugins" % ("scala-continuations-plugin_" + scalaVersion.value) % "1.0.1"),
"org.scala-lang.plugins" %% "scala-continuations-library" % "1.0.1")
case _ =>
Seq(compilerPlugin("org.scala-lang.plugins" % "continuations" % scalaVersion.value))
}
}

def dependencies = Seq(
"junit" % "junit" % "4.10" % "test",
"com.novocode" % "junit-interface" % "0.10" % "test",
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.6")

addSbtPlugin("com.github.gseitz" % "sbt-release" % "0.8.3")

addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8.2")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")

addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "0.2.1")

66 changes: 0 additions & 66 deletions src/jekyll/continuations.md

This file was deleted.

4 changes: 2 additions & 2 deletions src/jekyll/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ Links:
* [Basic Usage](usage.html)
* [Resource Type Class](resource.html)
* [Mechanics of map and flatMap](flatmap.html)
* [Delimited Continuations and ARM](continuations.html)
* [SocketExample](sockets.html)
* [Scaladoc API](latest/api/index.html)
* Browsable Source
Expand All @@ -18,9 +17,10 @@ You can find the library on the maven central repository.

groupId: com.jsuereth
artifactId: scala-arm_${scala-binary-version}
version: 1.3
version: 2.0


Note:
* Release can be found on maven central, or for the impatient: [Sonatype's OSSRH repository](https://oss.sonatype.org/content/groups/public/).
* Snapshots can be found on [Sonatype's OSSRH snapshot repository](https://oss.sonatype.org/content/groups/public).
* In the 2.0 version, all scala continuation based API has been removed, as the continuations plugin is now deprecated.
41 changes: 3 additions & 38 deletions src/jekyll/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ layout: default
title: Basic Usage
---

The Scala ARM library provides three "modes" of operations:
The Scala ARM library provides two "modes" of operations:

* Imperative style resource management functions.
* A monadic style resource management class.
* A delimited continuation style API.

## Imperative Style ##

Expand Down Expand Up @@ -80,43 +79,9 @@ The handy mechanism of `ManagedResource` is the ability to create a collection o
{% highlight scala %}
import scala.resource._
import java.io._
val stream = managed(new FileInputStream("text.txt"))
val reader = stream map (new BufferedReader(new InputStreamReader(_)))
val lines = stream map makeBufferedReaderLineIterator toTraversable
import java.nio.charset.Charset
val lines = Using.fileLines(Charset.defaultCharset)(new File("test.txt"))
lines.view map (_.trim) foreach println
{% endhighlight %}

Much of the noise in the example is dealing with the java.io API. The important piece is how we have a managed resource, convert it into a traversable and make some minor modification before aquiring. This produces a traversable that will eventually read the file. This allows us to pre-construct I/O related portions of our program to re-use over and over. For example, One could construct a `ManagedResource` that will read and parse configuration information. Then you can use a listener that detects when the file's modification date changes, and re-extract the configuration information from the `ManagedResource`.

## Delimited continuation style ##

The scala-arm library also supports using delimited continuations. This is done via the `reflect` method on `ManagedResource`. This can be used to "flatten" the nested blocks required to use resources. The best example is the `and` method defined on `scala.resource`. This method can be used to combine two resources into a single `ManagedResource` class containing a tuple of the two resources. It will jointly open and close both resources. The code is below:

{% highlight scala %}
import resource._
def and[A,B](r1 : ManagedResource[A], r2 : ManagedResource[B]) =
new ManagedResource[(A,B)] with ManagedResourceOperations[(A,B)] {
override def acquireFor[C](f : ((A,B)) => C) = withResources {
f( (r1.reflect[C], r2.reflect[C]) )
}
}
{% endhighlight %}

compare that with the previous "imperative style" `and` method:

{% highlight scala %}
def and[A,B](r1 : ManagedResource[A], r2 : ManagedResource[B]) : ManagedResource[(A,B)] =
new ManagedResource[(A,B)] with ManagedResourceOperations[(A,B)] {
override def acquireFor[C](f : ((A,B)) => C) : Either[List[Throwable], C] = {
val result = r1 acquireFor { opened1 =>
r2 acquireFor { opened2 =>
f((opened1, opened2))
}
}
result.fold( errors => Left(errors), y => y)
}
}
}
{% endhighlight %}

The mechanism for using Delimited Continuations is outlines in more detail on the [Delimited Continuations and ARM](continuations.html) page.
44 changes: 12 additions & 32 deletions src/main/scala/resource/ManagedResource.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import _root_.scala.collection.Traversable
import _root_.scala.collection.Iterator
import _root_.scala.Either
import _root_.scala.concurrent.{ ExecutionContext, Future }
import util.continuations.{suspendable, cps}

/**
* This class encapsulates a method of ensuring a resource is opened/closed during critical stages of its lifecycle.
Expand Down Expand Up @@ -47,7 +46,6 @@ trait ManagedResource[+R] {
* closed before returning.
*
* @param f The transformation function to apply against the raw resource.
* @param translator The translation implementation used to determine if we can extract from the ManagedResource.
*
* @return A new ManagedResource with the translated type or some other type if an appropriate translator was found.
*
Expand Down Expand Up @@ -78,6 +76,18 @@ trait ManagedResource[+R] {
*/
def acquireAndGet[B](f: R => B): B

/**
* Acquires the resource for the Duration of a given function, The resource will automatically be opened and closed.
* The result will be returned immediately, except in the case of an error. Upon error, the resource will be
* closed, and then the originating exception will be thrown.
*
* Note: This method will throw the last exception encountered by the managed resource, whatever this happens to be.
*
* @param f A function to execute against the handle returned by the resource
* @return The result of the passed in function
*/
def apply[B](f: R => B): B

/**
* Aquires the resource for the Duration of a given function, The resource will automatically be opened and closed.
* The result will be returned immediately in an Either container. This container will hold all errors, if any
Expand Down Expand Up @@ -117,36 +127,6 @@ trait ManagedResource[+R] {
* A resource that is a tupled combination of this and that.
*/
def and[B](that: ManagedResource[B]): ManagedResource[(R,B)]

/**
* Reflects the resource for use in a continuation. This method is designed to be used inside a
* <code>scala.resource.withResources</code> call.
*
* For example:
*
* <pre>
* import scala.resource._
* withResources {
* val output = managed(new FileInputStream("output.txt")).reflect[Unit]
* for(i <- 1 to 10) {
* val input = managed(new FileInputStream("sample"+i+".txt")).reflect[Unit]
* input lines foreach (output writeLine _)
* }
* }
* </pre>
* @return The raw resource, with appropriate continuation-context annotations.
*/
def reflect[B]: R @cps[Either[List[Throwable], B]]
/**
* Accesses this resource inside a suspendable CPS block
*/
@deprecated("Use now instead of !", "1.3")
def ! : R @suspendable

/**
* Accesses this resource inside a suspendable CPS block
*/
def now: R @suspendable
}


Expand Down
29 changes: 6 additions & 23 deletions src/main/scala/resource/ManagedResourceOperations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,26 @@

package resource

import _root_.scala.collection.Traversable
import _root_.scala.collection.TraversableOnce
import _root_.scala.util.continuations.{cps,shift, suspendable}
import _root_.scala.collection.{Traversable, TraversableOnce}
import _root_.scala.concurrent.{ ExecutionContext, Future }

/**
* This class implements all ManagedResource methods except acquireFor. This allows all new ManagedResource
* implementations to be defined in terms of the acquireFor method.
*/
trait ManagedResourceOperations[+R] extends ManagedResource[R] { self =>
//TODO - Can we always grab the top exception?
override def acquireAndGet[B](f: R => B): B = acquireFor(f).fold( liste => throw liste.head, x => x)
override def acquireAndGet[B](f: R => B): B = apply(f)
override def apply[B](f: R => B): B = acquireFor(f).fold( liste => throw liste.head, x => x)

def toTraversable[B](implicit ev: R <:< TraversableOnce[B]): Traversable[B] =
override def toTraversable[B](implicit ev: R <:< TraversableOnce[B]): Traversable[B] =
new ManagedTraversable[B,R] {
val resource = self
override protected def internalForeach[U](resource: R, g : B => U) : Unit =
ev(resource).foreach(g)
}

def toFuture(implicit context: ExecutionContext): Future[R] =
override def toFuture(implicit context: ExecutionContext): Future[R] =
Future(acquireAndGet(identity))

override def map[B](f : R => B): ExtractableManagedResource[B] =
Expand All @@ -45,25 +45,8 @@ trait ManagedResourceOperations[+R] extends ManagedResource[R] { self =>
}
override def toString = "FlattenedManagedResource[?](...)"
}

override def foreach(f: R => Unit): Unit = acquireAndGet(f)

override def and[B](that: ManagedResource[B]) : ManagedResource[(R,B)] = resource.and(self,that)

override def reflect[B]: R @cps[Either[List[Throwable], B]] = shift {
k: (R => Either[List[Throwable],B]) =>
acquireFor(k).fold(list => Left(list), identity)
}
// Some wierd intersection of scaladoc2 + continuations plugin forces us to be explicit about types here!
override def now: R @suspendable = shift { (k : R => Unit) =>
acquireAndGet(k)
()
}
// You may be asking why now and ! have the same implementation? Binary compatibility.... YIPIEE!!!
override def ! : R @suspendable = shift { (k : R => Unit) =>
acquireAndGet(k)
()
}
}


Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/resource/ManagedTraversable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
package resource

import _root_.scala.collection.Traversable
import scala.util.continuations._

/**
* This trait provides a means to ensure traversable access to items inside a resource, while ensuring that the
Expand Down Expand Up @@ -58,4 +57,6 @@ trait ManagedTraversable[+B, A] extends Traversable[B] {
//If there are errors -> Handle them appropriately
result.left foreach handleErrorsDuringTraversal
}
// Prevent toString from doing work.
override def toString = s"ManagedTraversable($resource)"
}
Loading

0 comments on commit d5c8446

Please sign in to comment.