These are chat archives for sirthias/swave

29th
Sep 2016
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 06:50
thanks a bunch, it works perfectly.
Are those .asInstanceOf casts really necessary though?
Mathias
@sirthias
Sep 29 2016 07:36
What casts do you mean?
Ah, I see, you mean the ones in the Streamable default implementations.
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 07:37
yes
Mathias
@sirthias
Sep 29 2016 07:38
No, they are not necessary in your code. They are required for a performance optimization, nothing more.
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 07:38
ohh :)
Mathias
@sirthias
Sep 29 2016 07:38
Essentially they allow the implicit instance to be allocated only once
i.e. cached
in your code you can simply turn the val into a def with a type param. Then you won't need any casts.
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 07:40
yes that was exactly my thought
Mathias
@sirthias
Sep 29 2016 07:41
the downside is, that a new instance will be created on every stream instantiation.
which is probably rare in your case
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 07:41
ohh
Mathias
@sirthias
Sep 29 2016 07:41
so no problme
and the instance is very light-weight, so nothing to worry about
still, on the swave-side of things we try to be as fast as possible, hence the caching
btw. what does your Streamable[EitherT[...]] implementation look like now?
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 07:47
package controllers
import cats.data.EitherT
import models.errors.LeroError
import swave.core.{Spout, Streamable}

import scala.concurrent.Future

trait SwaveStreamableTypeclasses {
  this: UploadControllerSwaveLike =>

  //scalastyle:off
  type Aux[T, Out0] = Streamable[T]
    {type Out = Out0}

  protected val futureStreamable =
    new Streamable[EitherT[Future, LeroError, AnyRef]] {
      type Out = AnyRef

      def apply(value: EitherT[Future, LeroError, AnyRef])
      : Spout[Either[LeroError, AnyRef]] =
        Spout.fromFuture(value.value)
    }

  implicit def forFuture[T]: Aux[EitherT[Future, LeroError, T], Either[LeroError, T]] =
    futureStreamable.asInstanceOf[Aux[EitherT[Future, LeroError, T], Either[LeroError, T]]]
}
value.value here is of type Future[Either[LeroError,AnyRef]]
Mathias
@sirthias
Sep 29 2016 07:49
I see, cool.
Btw.: you don't need to redefine Aux. You can also simply import Streamable.Aux.
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 07:50
ah yes, there were so many types to keep track of, so I just went with as-close-as-possible to your examples
ill just see if I can get rid of the cast
because casts make me sad
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 07:57
trait SwaveStreamableTypeclasses { this: UploadControllerSwaveLike =>
  import Streamable.Aux

  type EitherAux[T] = Aux[EitherT[Future, LeroError, T], Either[LeroError, T]]

  protected def futureStreamable[T]: EitherAux[T] =
    new Streamable[EitherT[Future, LeroError, T]] {
      type Out = Either[LeroError, T]
      def apply(value: EitherT[Future, LeroError, T]): Spout[Out] =
        Spout.fromFuture(value.value)
    }

  implicit def forFuture[T]: EitherAux[T] =
    futureStreamable[T]
}
type-defs don’t really make code more readable xD
anyway, no casts left now :)
Mathias
@sirthias
Sep 29 2016 08:02
You can be even shorter and more generic if you want with something like this:
implicit def forEitherT[M[_], L, R](implicit ev: Streamable[M[Either[L, R]]]): Streamable.Aux[EitherT[M, L, R], Either[L, R]] =
    new Streamable[EitherT[M, L, R]] {
      type Out = AnyRef
      def apply(value: EitherT[M, L, R]): Spout[Either[L, R]] = ev(value.value)
    }
not tested, but it should work I think
so this would work for all EitherT types
one mistake: type Out = AnyRef should be type Out = Either[L, R]
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 08:40
I'll have a look at it soon seems like the better way to go
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 11:40
what’s ev?
ah nvm
I don’t get why ev(value.value) should get me a Spout[Either[L,R]]
ahh maybe I do
but it doesn’t compile
Error:(14, 67) type mismatch; found : swave.core.Spout[ev.Out] required: swave.core.Spout[Either[L,R]] def apply(value: EitherT[M, L, R]): Spout[Either[L, R]] = ev(value.value)
I think the type of ev is wrong here
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 11:48
I tried changing it to this, which compiles:
  implicit def forEitherT[M[_], L, R](implicit ev: Streamable.Aux[M[Either[L, R]],Either[L,R]])
    : Streamable.Aux[EitherT[M, L, R], Either[L, R]] =
    new Streamable[EitherT[M, L, R]] {
      type Out = Either[L,R]
      def apply(value: EitherT[M, L, R]): Spout[Either[L, R]] = ev(value.value)
    }
however, then it doesn’t find the instance I need anymore
with the following error
Error:(31, 46) could not find implicit value for parameter ev: swave.core.Streamable[cats.data.EitherT[scala.concurrent.Future,models.errors.LeroError,List[org.apache.commons.vfs2.FileObject]]]{type Out = B}
    .sub.map(moveToCodeSigning).flattenConcat().end
I would suspect this to work whenever there’s an instance of i.e. Streamable[Future[T]] { Out = Future[T] }
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 11:53
oh wait
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 12:01
ahh it was another issue preventing the trait from being found (weird)
anyway, the above code actually works
Mathias
@sirthias
Sep 29 2016 12:57
Ah, yes, sorry. My snippet had a Streamable implicit requirement, where it shoudl have been a Streamable.Aux.
But you found this yourself.
So you now have a fully generic Streamable implementation for EitherT.
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 13:32
Yes -- it's cool
It seems incredibly powerful when combined with cats!
Mathias
@sirthias
Sep 29 2016 13:33
Yes. The idea is to build abstractions for common problems only once and make them nicely combinable.
As such there are quite a few similarities with cats.
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 13:33
Are there any best practices regarding error handling between stages of a stream?
Mathias
@sirthias
Sep 29 2016 13:34
You basically have two choices: Either keep the errors inside of the stream elements as part of your types
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 13:34
Our gut feeling so far is to either propagate errors to the very end of the stream or filter them
Mathias
@sirthias
Sep 29 2016 13:34
or use the error channel built into the streaming infrastructure
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 13:35
Any information on that error channel?
Mathias
@sirthias
Sep 29 2016 13:35
in the latter case the first error will stop the stream and clean up all resources
you could use things like recover to deal with errors, but you have to be aware of the fact that elements might be skipped in the process.
for example, if you map a Spout and your mapping function throws an exception
then this exception will "kill" the stream, cause all resources to be cleaned up, and yield an error at all drains
(if the drains are downstream)
Felix Palludan Hargreaves
@hejfelix
Sep 29 2016 14:48
I think we'll stick to Either