These are chat archives for typelevel/cats

6th
Dec 2017
Kai(luo) Wang
@kailuowang
Dec 06 2017 02:49

hmmm, Arrow forms a Semigroupal

  implicit def catsSemigroupalForArrow[F[_, _], A](implicit F: Arrow[F]): Semigroupal[F[A, ?]] = new Semigroupal[F[A, ?]] {
    def product[B, C](fb: F[A, B], fc: F[A, C]): F[A, (B, C)] =
      F.andThen(F.lift((x: A) => (x, x)), F.split(fb, fc))
  }

Whatelse does it form? and why we don't have the above in the Cats?

Stephen Lazaro
@stephen-lazaro
Dec 06 2017 03:20
I think Semigroupal is as much as you get for free. There’s a Monoidal version apparently:
https://hackage.haskell.org/package/base-4.10.1.0/docs/Control-Arrow.html#t:ArrowZero
Which any MonadPlus yields an instance of
Julian Michael
@julianmichael
Dec 06 2017 05:38
couple quick questions: 1) why doesn't Arrow provide lift(identity) as a default implementation of Category's id? and 2) any thoughts on having typeclass instances for PartialFunction? I was looking for a Profunctor instance for Map and the rabbit hole basically ended there...
though f: PartialFunction[A, B] can be converted to something more usable with, like, Kleisli(f.lift) I guess. But it seems in the spirit of cats to provide instances for stdlib types so they're usable with minimal friction
zero_coder
@kostonstyle
Dec 06 2017 09:42

Hi all
I have a function that do monadic recursion:

  private def pool[F[_] : Monad : Foldable, A]
  (consumer: => Consumer[String, String])(cb: Vector[KkConsumerRecord] => F[A]): IO[Unit] = {
    val records: ConsumerRecords[String, String] = consumer.poll(Long.MaxValue)
    val converted = records.iterator().asScala.map(rec => {
      KkConsumerRecord(rec.key(),
        rec.value(),
        rec.offset(),
        rec.partition(),
        rec.topic(),
        rec.timestamp())
    })

    val vec = converted.foldLeft(Vector.empty[KkConsumerRecord]) { (b, a) =>
      a +: b
    }

    Foldable[F].foldM(cb(vec), ()) { (_, _) =>
      pool(consumer)(cb)
    }

  }

As you can see the last expression do fold on the foldable monad toIO monad. I want that (cb: Vector[KkConsumerRecord] => F[A]) return an IO[A] but the IO is not foldable. Inside cb it could generates some side effects, that why I want a return type of IO.
How to do monadic recursion without fold?
Thanks

Alexander Konovalov
@alexknvl
Dec 06 2017 11:06
@kostonstyle return IO[F[A]]
zero_coder
@kostonstyle
Dec 06 2017 12:53
like cb: Vector[KkConsumerRecord] => IO[F[A]]? Remember, I want to do monadic recursion
Kai(luo) Wang
@kailuowang
Dec 06 2017 13:06
@stephen-lazaro thanks.
@julianmichael I don't know why it shouldn't, in fact there is this law in ArrowLaws
 def arrowIdentity[A]: IsEq[F[A, A]] =    F.lift(identity[A]) <-> F.id[A]
I can add that or will you be interested in adding it really quick?
As for PartialFunction , we did have several discussions in the past, but there is some distaste for it. But I think it should be perfectly fine to add to alleycats (now a module inside cats)
and alleycats doesn't have a dec 12 deadline. so we have more time.
Rafał Krzewski
@rkrzewski
Dec 06 2017 13:20

Hi all, I'm struggling with a problem that involves EitherT. I though maybe some could give my a hand?

trait Failure // an ADT representing failures in my application
type Action[T] = cats.data.EitherT[monix.eval.Task, Failure, T]
def f(A): Action[B] = // a specific action, A, B types are unimportant here
val as: List[A]

When I'm doing

val bs: Action[List[B]] = as.traverse(f)

and f yields a Failure for any elements in as, bs action also yields Failure which is great. However, there's particular spot in my program when I want to do something else, namely apply f to all elements of as and collect only the produced Bs ignoring any Failures. The expected type Action[List[B]] is the same but semantics are different. I was able to come up with the following so far:

def traverseWithRecover[A, B](as: List[A])(f: A => Action[B]): Action[List[B]] =
  as.traverse { a =>
    f(a).map(Some(_): Option[B]).recover { case _ => None }
  }.map { bs =>
    bs.collect { case Some(b) => b }
  }

It works as expected, but I'm sure this is not the shortest and most elegant way to achieve that!

Luka Jacobowitz
@LukaJCB
Dec 06 2017 13:37
I think you can do `as.map(f). collect {
Ah damnit gitter
Gonna post
This later when I'm on a PC
Rafał Krzewski
@rkrzewski
Dec 06 2017 13:39
Sure, thanks!
Luka Jacobowitz
@LukaJCB
Dec 06 2017 14:07
as.traverse(f andThen(_.value)).map(_.collect { case Right(r) => r })
This gives you a Task[List[B]]
Rafał Krzewski
@rkrzewski
Dec 06 2017 14:13
Um, I need an EitherT[Task, Failure, List[B]] so I'll need to map the List[B] to a Right[List[B]] and then wrap the Task in an EitherT
Cody Allen
@ceedubs
Dec 06 2017 14:27
@rkrzewski it might not be the most efficient solution, but I think that you could also do something like: as.traverse(a => f(a).attempt).map(_.separate) to get back a Task[(List[Throwable], List[B])]
oh sorry I didn’t realize Action was an EitherT and not just a Task. That wouldn’t be quite right then.
Rafał Krzewski
@rkrzewski
Dec 06 2017 14:30
@ceedubs yeah, first I had to change to as.traverse(a => f(a).value.attempt).map(_.separate)
but then not enough arguments for method separate: (implicit F: cats.Monad[List], implicit A: cats.Alternative[List], implicit G: cats.Bifoldable[Either])(List[Throwable], List[Either[dbdaemon.Failure,B]]). Unspecified value parameter G.
Cody Allen
@ceedubs
Dec 06 2017 14:46
@rkrzewski hmm I don’t see right away why you are getting that error. I’d have to poke around a bit. But I hadn’t read through your problem carefully enough. Are you attempting to handle the Throwable from a failed Task, or only the potential Failure from the EitherT?
@rkrzewski did you use the uber import (import cats.implicits._) or piecemeal imports?
Rafał Krzewski
@rkrzewski
Dec 06 2017 14:47
@ceedubs I'm trying to traverse a list of inputs with an A => Action[B] but instead of stopping at first failed action, collect results of all successful actions producing Action[List[B]]
@ceedubs piecemeal
Cody Allen
@ceedubs
Dec 06 2017 14:48
you might be missing the either instances
Rafał Krzewski
@rkrzewski
Dec 06 2017 14:48
but @LukaJCB 's solution is reasonably compact, so I can just go with that. Thanks for your time!
Cody Allen
@ceedubs
Dec 06 2017 14:48
okay :thumbsup:
Joe Ferris
@jferris
Dec 06 2017 16:25
if I have EitherT[F, A, B] and a function from A => F[B], what's the best way to end up with F[B]? I've found handleErrorWith, value, and valueOr, but all of those seem like I'd have to combine a few things together
I think I want something like valueOrF but maybe I'm missing something better
Luka Jacobowitz
@LukaJCB
Dec 06 2017 16:27
I think you can just do something like
f andThen EitherT.liftF
Oh wait
you want an F[B]
Kai(luo) Wang
@kailuowang
Dec 06 2017 16:27
@ceedubs also a quick review on #2065 will help us debugging build going forward.
:)
Cody Allen
@ceedubs
Dec 06 2017 16:28
@kailuowang :thumbsup: I was reviewing #2065 but am taking a quick detour into #2063. I should complete both reviews shortly.
Kai(luo) Wang
@kailuowang
Dec 06 2017 16:29
thanks so much!
Loïc Girault
@lgirault
Dec 06 2017 16:29
eitherT.bimap(f,identity) ?
wait no
Luka Jacobowitz
@LukaJCB
Dec 06 2017 16:29
You’ll need to fold the EitherT
val fb: F[B] = eitherT.flatMap(f andThen EitherT.liftF).fold(handleRightSide, identity)
Joe Ferris
@jferris
Dec 06 2017 16:38
interesting
Luka Jacobowitz
@LukaJCB
Dec 06 2017 16:47
and handleRightSide would have to be an A => B
Kai(luo) Wang
@kailuowang
Dec 06 2017 16:48
@julianmichael I ended up submitting the PR for making lift(identity) default Id #2072
Joe Ferris
@jferris
Dec 06 2017 18:50
my A and B don't have a common ancestor for merge - it works with F[A] and F[B] because my F is Future
I'm trying to map server responses into "this worked and I got A", "this didn't work but you should retry," or "this didn't work and is a bug (Future.failed)
Stephen Duncan Jr
@jrduncans
Dec 06 2017 18:52
I thought your f was transforming your A to a F[B]?
Joe Ferris
@jferris
Dec 06 2017 18:52
it does, and Future.failed(exception) is a valid F[B]
Stephen Duncan Jr
@jrduncans
Dec 06 2017 19:01
Not sure if I'm still missing something from your requirements, or if my solution was unclear:
val f: String => Future[Int] = _ => Future.successful(0)

EitherT.right[String](Future.failed[Int](new RuntimeException("boom"))).leftSemiflatMap(f).merge
res6: Future[Int] = Future(Failure(java.lang.RuntimeException: boom))

EitherT.leftT[Future, Int]("err").leftSemiflatMap(f).merge
res7: Future[Int] = Future(Success(0))

EitherT.rightT[Future, String](7).leftSemiflatMap(f).merge
res8: Future[Int] = Future(Success(7))

EitherT.leftT[Future, Int]("err").leftSemiflatMap(_ => Future.failed(new RuntimeException("recover-boom"))).merge
res9: Future[Int] = Future(Failure(java.lang.RuntimeException: recover-boom))
Joe Ferris
@jferris
Dec 06 2017 19:05
oh, I may have missed something - let me check
ah, because leftSemiflatMap lets you change the A type, which means merge makes sense
Joe Ferris
@jferris
Dec 06 2017 19:11
cool, that does work
Edd Steel
@eddsteel
Dec 06 2017 20:05
I figure there should be a neat way to do Seq[A => A] => (A => A), but I can't find it. I had assumed A => A was a monoid and I could combineAll, but it isn't
is there a better way than to fold with identity and andThen?
Harrison Houghton
@hrhino
Dec 06 2017 20:09
Weird, cats doesn't have Endo...
(that would be the monoid you want)
maybe you need to turn your Seq into something with a Foldable instance?
Edd Steel
@eddsteel
Dec 06 2017 20:11
I think that still needs the monoid though?
Harrison Houghton
@hrhino
Dec 06 2017 20:11
Yeah. The code I linked means that A => A is a monoid for any A.
Can you make a scalafiddle?
Edd Steel
@eddsteel
Dec 06 2017 20:12
ohh, my bad, I thought that was A => B without clicking :)
I'll try to create a minimal case then, probably doing something stupid
Harrison Houghton
@hrhino
Dec 06 2017 20:12
(A => B is a monoid if B is, but I don't think that's in cats...)
Luka Jacobowitz
@LukaJCB
Dec 06 2017 20:16
It is in cats (:
Edd Steel
@eddsteel
Dec 06 2017 20:19
https://scalafiddle.io/sf/9ft7ZHX/0 -- seems to be using the Monoid for B?
Kai(luo) Wang
@kailuowang
Dec 06 2017 20:19
it is.
Edd Steel
@eddsteel
Dec 06 2017 20:19
so I'm summing the results of my int endos instead of composing them
Edd Steel
@eddsteel
Dec 06 2017 20:20
sounds like I need to learn about MonoidK :)
Kai(luo) Wang
@kailuowang
Dec 06 2017 20:21
so if you want that instance you can use MonoidK[λ[α => Function1[α, α]]. algebra
Edd Steel
@eddsteel
Dec 06 2017 20:23
The MonoidK docs are nice and clear, it makes me wonder if there is a composeAll analogous to combineAll
Luka Jacobowitz
@LukaJCB
Dec 06 2017 20:23
There is
it’s called foldK
Kai(luo) Wang
@kailuowang
Dec 06 2017 20:31

interestingly

val l: List[Int => Int] = List((i: Int) => i * 2, (i: Int) => i * 3)
​l.foldK

gives you

could not find implicit value for parameter G: cats.MonoidK[[+R]Int => R]

type Endo[A] = A => A

val l: List[Endo[Int]] = List((i: Int) => i * 2, (i: Int) => i * 3)

l.foldK
is okay
Edd Steel
@eddsteel
Dec 06 2017 20:32
OK I thought it was just me :D
Luka Jacobowitz
@LukaJCB
Dec 06 2017 20:32
Huh
Rob Norris
@tpolecat
Dec 06 2017 20:33
urk
Edd Steel
@eddsteel
Dec 06 2017 20:33
also I'm getting a different answer https://scalafiddle.io/sf/9ft7ZHX/2
Kai(luo) Wang
@kailuowang
Dec 06 2017 20:35
huh?
I don’t understand how foldLeft gives you 2
oh, it’s the reverse order
monoidK for endo is using compose while you re using andThen
Rob Norris
@tpolecat
Dec 06 2017 20:39
I think this is probably not what most people want.
Edd Steel
@eddsteel
Dec 06 2017 20:40
ah, makes sense
which is not what most people want? @tpolecat
Fabio Labella
@SystemFw
Dec 06 2017 20:42
didn't I open an issue for this, to make it behave the same as kleisli
and then it turns out you can't?
Rob Norris
@tpolecat
Dec 06 2017 20:42
In Scala I think andThen comes more naturally than compose, because we're used to foo(a).bar(b)
Fabio Labella
@SystemFw
Dec 06 2017 20:42
or am I getting confused with A => B?
Edd Steel
@eddsteel
Dec 06 2017 20:42
:+1: that's my intuition
Fabio Labella
@SystemFw
Dec 06 2017 20:42
ah no, sorry
it's just the order of compose
fair enough
Luka Jacobowitz
@LukaJCB
Dec 06 2017 20:44
I think compose is the way Haskell does it, which is probably why we have it
Kai(luo) Wang
@kailuowang
Dec 06 2017 20:44
it’s derived from Category, so compose makes sense.
Luka Jacobowitz
@LukaJCB
Dec 06 2017 20:45
But yeah, >>> does seem more useful
Rob Norris
@tpolecat
Dec 06 2017 20:46
I'm not arguing either way, I have just been doing this long enough to know that everyone thinks it's backwards when they first see it.
It came up on #scalaz from time to time.
Edd Steel
@eddsteel
Dec 06 2017 20:47
It certainly makes me think my code would be clearer with an explicit fold. Is fold right +
Is fold right plus compose equivalent? Sorry, phone
Kai(luo) Wang
@kailuowang
Dec 06 2017 20:49
looks like we need a reduceRightK
Julian Michael
@julianmichael
Dec 06 2017 20:51
@kailuowang thanks for the answers! And nice, that was fast :)
Kai(luo) Wang
@kailuowang
Dec 06 2017 20:51
np
speaking of #2072 @LukaJCB I dismissed the review with some mima exceptions and scalastyle fix, you mind to a quick re-approval?
Luka Jacobowitz
@LukaJCB
Dec 06 2017 20:53
Done!
Kai(luo) Wang
@kailuowang
Dec 06 2017 20:53
thanks!
so I think we need two things, a reduceRightK and an Endo type alias
Edd Steel
@eddsteel
Dec 06 2017 21:07
In any case this was very enlightening. Thanks all!
Kai(luo) Wang
@kailuowang
Dec 06 2017 21:11
you are welcome
Edd Steel
@eddsteel
Dec 06 2017 21:25
(side note, for this particular issue a colleague just pointed out Function.chain in the stdlib)