Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Future and try tap each #45

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/main/scala/scala/concurrent/next/FutureExtensions.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package scala.concurrent.next

import scala.concurrent.{Future, ExecutionContext}
import scala.util.next.TryExtensions._

object FutureExtensions{
implicit class FutureExt[T](ft: Future[T]){

/** Applies a side-effecting function to the encapsulated value in this Future
*
* Calling `tapEach` on `Failure` will not execute `f`
*
* If the side-effecting function fails, the exception will be
* propagated as a `Failure` and the current value of
* `Success` will be discarded. For example the following will result
* in a `RuntimeException` and will not reach `map`
*
* {{{
* val f : Future[Int] = Future.successful(5)
*
* f
* .tapEach(throw new RuntimeException("runtime exception"))
* .map(_ + 1)
* }}}
*
* @param f a function to apply to each element in this Future
* @tparam U the return type of f
* @return The same Future as this
*/
def tapEach[U](f: T => U)(implicit executor: ExecutionContext): Future[T] = ft.transform(_ tapEach f)
}
}
51 changes: 51 additions & 0 deletions src/main/scala/scala/util/next/TryExtensions.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package scala.util.next

import scala.util.{Try, Failure, Success}
import scala.util.control.NonFatal

object TryExtensions{

implicit class TryExtensions[T](t: Try[T]) {

/** Applies a side-effecting function to the encapsulated value in this Try
*
* Calling `tapEach` on `Failure` will not execute `f`
*
* If the side-effecting function fails, the exception will be
* propagated as a `Failure` and the current value of
* `Success` will be discarded. For example the following will result
* in a `RuntimeException` and will not reach `map`
*
* {{{
* val t : Try[Int] = Success(5)
*
* t.tapEach(throw new RuntimeException("runtime exception")).map(_ + 1)
* }}}
*
* @param f a function to apply to each element in this Future
* @tparam U the return type of f
* @return The same Try as this
*/
def tapEach[U](f: T => U) = t match{
case _ : Failure[T] => t.asInstanceOf[Try[T]]
case _ : Success[T] => try {
f(t.get)
t
} catch{
case NonFatal(e) => Failure[T](e)
}
}
}
}
56 changes: 56 additions & 0 deletions src/test/scala/scala/util/next/TryExtensionsTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package scala.util.next

import org.junit.Assert._
import org.junit.Test
import scala.util.{Try, Success, Failure}


final class TryExtensionsTest{

import TryExtensions._

@Test
def trySuccessTapEachTest(): Unit = {

val succInt: Try[Int] = Success[Int](5)
var num = 3

def tapAndMutate(t: Try[Int]) ={
t.tapEach(a => {num = 5} ).map(_ + num)
}

assertEquals(Success(10), tapAndMutate(succInt))
}

@Test
def tryFailureTapEachTest(): Unit = {
val failInt: Try[Int] = Failure[Int](new RuntimeException("run time exception"))
var num = 3
failInt.tapEach(a => {num = 5})
assertEquals(3, num)
}


@Test
def tryFailingSideEffectTapEachTest() : Unit = {
val succInt: Try[Int] = Success[Int](5)
val e = new RuntimeException("run time exception")
val newSucc: Try[Int] = succInt
.tapEach(_ => throw e)
.map(_ + 1)

assertEquals(Failure(e), newSucc)
}
}