I was in the middle of Tasmania last week, sorry for not being able to
make the meeting!
I've had bugs in every project that I've worked on which used Task,
due to the API. Some problems I remember:
1. Confusion around differences in Task.delay, Task.apply, Task.now
2. Implicit ExecutorService
3. Implicit Strategy
4. Side-effect (during initialisation of the Strategy companion object)
https://github.com/scalaz/scalaz/blob/scalaz-seven/concurrent/src/main/scala/scalaz/concurrent/Strategy.scala#L25
5. Non-parametric type:
https://github.com/scalaz/scalaz/blob/scalaz-seven/concurrent/src/main/scala/scalaz/concurrent/Strategy.scala#L15
Now, we could just fix each of the above problems, and whatever others
people have, but I question why Task should be used at all. The teams
I work with try to use Task as a replacement for
scalaz.effect.IO
because it has a couple of primitive concurrency combinators.
I wonder if it'd be more useful to add concurrency combinators to
scalaz.effect.IO instead. I've had a go at a start:
https://github.com/scalaz/scalaz/compare/series/7.3.x...puffnfresh:feature/concurrent-io
I think this is a reasonable direction. I would like either scalaz 7.4
or scalaz 8.0 to provide all the combinators for replacing
scalaz.concurrent.Task with
scalaz.effect.IO but I have questions:
* Do people use Task for other reasons?
* What API would people need to be able to replace Task with IO?
* Does IO give less performance for these things than Task?
Outside of IO, I think the rest of Tony's Gist looks great. I would
like to advocate "liftCvf" existing, as described in Stephen Compall's
"When can Liskov be lifted?" post:
http://typelevel.org/blog/2014/03/09/liskov_lifting.html
With that combinator, we should be able to write an invariant Maybe
data type, which shares a singleton Empty object:
private case object Empty extends Maybe[Nothing]
object Maybe {
def empty[A]: Maybe[A] =
Liskov.liftCvf(isa[A, Nothing])(Empty)
implicit def MaybeFunctor: Functor[Maybe] =
???
}
Maybe.empty should be a full Prism, but hopefully you get the idea.
Using this single Liskov combinator should be theoretically sound,
although the implementation would be a cast. I think it is necessary
if we want to "do better than scala-library.jar" in terms of
performance and memory consumption.