Related
I have these types:
SomeTypeClass
A higher kinded type which has one type parameter of kind * => * => *
trait SomeTypeClass[P[_, _]] {
def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
(pab: P[A, B])
(implicit ev: Strong[P],
ev2: Choice[P],
ev3: Applicative[F]): P[S, T]
}
Target which accepts three type parameters: type constructor F[_] and two polymorphic types A, B
case class Target[F[_], A, B](f: A => F[B])
I want to implement an instance of SomeTypeClass of Target.
I am using the kind-projector plugin in order to create a partially applied type.
My desired method signature should be:
implicit def instance: SomeTypeClass[Target[F, *, *]] = new SomeTypeClass[Target[F, *, *]] {
override def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
(pab: Target[F, A, B])
(implicit ev: Strong[Target[F, *, *]],
ev2: Choice[Target[F, *, *]],
ev3: Applicative[F]): Target[F, S, T] = ???
}
I've tried using this syntax using two star parameters:
implicit def instance[F[_]]: SomeTypeClass[Target[F, *, *]] = new SomeTypeClass[Target[F, *, *]] {
override def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
(pab: Target[F, A, B])
(implicit ev: Strong[Target[F, *, *]],
ev2: Choice[Target[F, *, *]],
ev3: Applicative[F]): Target[F, S, T] = ???
}
But the F[_] declared at the instance level shadows the F[_] declared at the test method (I want them to be the same F), so I've moved to the λ syntax and got two different unwanted results.
The first one using λ[(F, A, B) => Target[F, A, B]] generated for the pab paramter,
pab: Target[A, B, B] instead of pab: Target[F, A, B] and also for the return type Target[S, T, B] instead of Target[F, S, T]
The second one using the F at the end of the triple type lambda parameters (why???) λ[(A, B, F) => Target[F, A, B]] generated the correct types for the pab parameter and the return type, but
for each one of the implicit parameters the type Strong[λ[(A, B, F) => Target[F, A, B]]] instead of
Strong[Target[F, *, *]]]
The full code:
import cats.Applicative
import cats.arrow.{Choice, Strong}
final case class Target[F[_], A, B](f: A => F[B])
trait SomeTypeClass[P[_, _]] {
def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
(pab: P[A, B])
(implicit ev: Strong[P],
ev2: Choice[P],
ev3: Applicative[F]): P[S, T]
}
object SomeTypeClass {
implicit def instance1: SomeTypeClass[λ[(F, A, B) => Target[F, A, B]]] = new SomeTypeClass[λ[(F, A, B) => Target[F, A, B]]] {
override def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
(pab: Target[A, B, B])
(implicit ev: Strong[Target],
ev2: Choice[Target],
ev3: Applicative[F]): Target[S, T, B] = ???
}
implicit def instance2: SomeTypeClass[λ[(A, B, F) => Target[F, A, B]]] = new SomeTypeClass[λ[(A, B, F) => Target[F, A, B]]] {
override def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
(pab: Target[F, A, B])
(implicit ev: Strong[λ[(A, B, F) => Target[F, A, B]]],
ev2: Choice[λ[(A, B, F) => Target[F, A, B]]],
ev3: Applicative[F]): Target[F, S, T] = ???
}
}
Can I achieve the desired syntax using this plugin? Why does the plugin generate different types
for different order of type lambda's 'parameters'?
If I understood
But the F[_] declared at the instance level shadows the F[_] declared at the test method (I want them to be the same F)
correctly, you want your instance for SomeTypeClass[Target[...]] to fix the F[_] parameter of test. But that's simply not possible with this test type signature. Once you have (for example)
val inst = implicitly[SomeTypeClass[Target[...]]
you can call
val res1 = inst.test[List, ...]
val res2 = inst.test[Option, ...]
Type lambdas don't offer a way around this problem. You need to either move F[_] parameter to SomeTypeClass or implement
implicit def instance[F[_]]: SomeTypeClass[Target[F, *, *]] = new SomeTypeClass[Target[F, *, *]] {
override def test[G[_], S, T, A, B](f: (A => G[B]) => S => G[T])
(pab: Target[F, A, B])
(implicit ev: Strong[Target[F, *, *]],
ev2: Choice[Target[F, *, *]],
ev3: Applicative[G]): Target[G, S, T] = ???
}
which I expect is impossible as you can't pass pab.f to f.
EDIT: the type of wander
class (Choice p, Strong p) => Traversing p where
traverse' :: Traversable f => p a b -> p (f a) (f b)
traverse' = wander traverse
wander :: (forall f. Applicative f => (a -> f b) -> s -> f t) -> p a b -> p s t
wander f pab = dimap (\s -> Baz $ \afb -> f afb s) sold (traverse' pab)
is a rank-2 type which aren't supported in Scala directly; instead you need to introduce a helper (which can't just be a type alias as it is in Control.Lens.Type)
trait Traversal[S, T, A, B] {
def apply[F[_]: Applicative](f: A => F[B]): S => F[T]
}
Then
trait Traversing[P[_, _]] extends Strong[P] with Choice[P] {
def wander[S, T, A, B](t: Traversal[S, T, A, B], pab: P[A, B]): P[S, T]
}
implicit def instance[F[_]: Applicative]: Traversing[Target[F, *, *]] = new Traversing[Target[F, *, *]] {
def wander[S, T, A, B](t: Traversal[S, T, A, B], pab: Target[F, A, B]): Target[F, S, T] = Target(t(pab.f))
// define Strong and Choice methods too
}
should work. (Though I am not sure this is the cats way to deal with Strong and Choice requirements.)
I'd like to build the equivalent of:
def applyWithHList2[A1, A2, R, L <: HList](l: L, f: Function2[A1, A2, R]): Try[R]
The values in the list are such that in the N choose 2 possible value combinations of l.unify there is at most one that could be used to call the function. No additional type information is available.
If there is no way to call the function, the result should be Failure with MatchError. Otherwise, the result should be Try(f(a1, a2)).
I am still getting used to shapeless and would appreciate suggestions for how to approach this problem.
Funnily enough it's a lot easier to write a version that just doesn't compile if appropriately typed elements aren't available in the HList:
import shapeless._, ops.hlist.Selector
def applyWithHList2[A1, A2, R, L <: HList](l: L, f: (A1, A2) => R)(implicit
selA1: Selector[L, A1],
selA2: Selector[L, A2]
): R = f(selA1(l), selA2(l))
If you really want a runtime error (in a Try) for cases where there's not an applicable pair, you could use the default null instance trick:
import scala.util.{ Failure, Success, Try }
def applyWithHList2[A1, A2, R, L <: HList](l: L, f: (A1, A2) => R)(implicit
selA1: Selector[L, A1] = null,
selA2: Selector[L, A2] = null
): Try[R] = Option(selA1).flatMap(s1 =>
Option(selA2).map(s2 => f(s1(l), s2(l)))
).fold[Try[R]](Failure(new MatchError()))(Success(_))
If you find that unpleasant (and it is), you could use a custom type class:
trait MaybeSelect2[L <: HList, A, B] {
def apply(l: L): Try[(A, B)] = (
for { a <- maybeA(l); b <- maybeB(l) } yield (a, b)
).fold[Try[(A, B)]](Failure(new MatchError()))(Success(_))
def maybeA(l: L): Option[A]
def maybeB(l: L): Option[B]
}
object MaybeSelect2 extends LowPriorityMaybeSelect2 {
implicit def hnilMaybeSelect[A, B]: MaybeSelect2[HNil, A, B] =
new MaybeSelect2[HNil, A, B] {
def maybeA(l: HNil): Option[A] = None
def maybeB(l: HNil): Option[B] = None
}
implicit def hconsMaybeSelect0[H, T <: HList, A](implicit
tms: MaybeSelect2[T, A, H]
): MaybeSelect2[H :: T, A, H] = new MaybeSelect2[H :: T, A, H] {
def maybeA(l: H :: T): Option[A] = tms.maybeA(l.tail)
def maybeB(l: H :: T): Option[H] = Some(l.head)
}
implicit def hconsMaybeSelect1[H, T <: HList, B](implicit
tms: MaybeSelect2[T, H, B]
): MaybeSelect2[H :: T, H, B] = new MaybeSelect2[H :: T, H, B] {
def maybeA(l: H :: T): Option[H] = Some(l.head)
def maybeB(l: H :: T): Option[B] = tms.maybeB(l.tail)
}
}
trait LowPriorityMaybeSelect2 {
implicit def hconsMaybeSelect2[H, T <: HList, A, B](implicit
tms: MaybeSelect2[T, A, B]
): MaybeSelect2[H :: T, A, B] = new MaybeSelect2[H :: T, A, B] {
def maybeA(l: H :: T): Option[A] = tms.maybeA(l.tail)
def maybeB(l: H :: T): Option[B] = tms.maybeB(l.tail)
}
}
And then:
def applyWithHList2[A1, A2, R, L <: HList](l: L, f: (A1, A2) => R)(implicit
ms2: MaybeSelect2[L, A1, A2]
): Try[R] = ms2(l).map(Function.tupled(f))
But that's a lot of work just to throw away some compile-time safety.
Note that none of these approaches enforce the constraint that there's only at most pair of elements in the HList that the function can be applied to, since I read that as a pre-condition in your question. It'd definitely be possible to write a solution that enforced the constraint at compile time (and it might even be a bit shorter than the MaybeSelect2 implementation above).
This is a followup to my previous question with an example found on the Internet.
Suppose I define a typeclass Applicative as follows:
trait Functor[T[_]]{
def map[A,B](f:A=>B, ta:T[A]):T[B]
}
trait Applicative[T[_]] extends Functor[T] {
def unit[A](a:A):T[A]
def ap[A,B](tf:T[A=>B], ta:T[A]):T[B]
}
I can define an instance of Applicative for List
object AppList extends Applicative[List] {
def map[A,B](f:A=>B, as:List[A]) = as.map(f)
def unit[A](a: A) = List(a)
def ap[A,B](fs:List[A=>B], as:List[A]) = for(f <- fs; a <- as) yield f(a)
}
For convenience I can define an implicit conversion to add a method <*> to List[A=>B]
implicit def toApplicative[A, B](fs: List[A=>B]) = new {
def <*>(as: List[A]) = AppList.ap(fs, as)
}
Now I can do a cool thing !
zip two lists List[String] and apply f2 to every pair in applicative style
val f2: (String, String) => String = {(first, last) => s"$first $last"}
val firsts = List("a", "b", "c")
val lasts = List("x", "y", "z")
scala> AppList.unit(f2.curried) <*> firsts <*> lasts
res31: List[String] = List(a x, a y, a z, b x, b y, b z, c x, c y, c z)
So far, so good but now I have:
val firstsOpt = Some(firsts)
val lastsOpt = Some(lasts)
I would like to zip firsts and lasts, apply f2, and get Option[List[String]] in applicative style. In other words I need <*> for Option[List[_]]. How can I do it ?
Firstly, you need an instance of applicative for Option:
implicit object AppOption extends Applicative[Option] {
def map[A, B](f: A => B, o: Option[A]) = o.map(f)
def unit[A](a: A): Option[A] = Some(a)
def ap[A, B](of: Option[A => B], oa: Option[A]) = of match {
case Some(f) => oa.map(f)
case None => None
}
}
Then you can also create an applicative instance for the composition of two applicatives (note, based on the Haskell version):
class AppComp[F[_], G[_]](fa: Applicative[F], ga: Applicative[G]) extends Applicative[({ type f[A] = F[G[A]]})#f] {
def map[A, B](f: A => B, a: F[G[A]]): F[G[B]] = fa.map((g: G[A]) => ga.map(f, g), a)
def unit[A](a: A) = fa.unit(ga.unit(a))
def ap[A, B](f: F[G[A => B]], a: F[G[A]]): F[G[B]] = {
val liftg: G[A => B] => (G[A] => G[B]) = gf => (gx => ga.ap(gf, gx))
val ffg: F[G[A] => G[B]] = fa.map(liftg, f)
fa.ap(ffg, a)
}
}
implicit def toComp[F[_], G[_]](implicit fa: Applicative[F], ga: Applicative[G]) = new AppComp[F, G](fa, ga)
Finally you can now do:
val ola = toComp[Option, List]
ola.ap(ola.ap(ola.unit(f2.curried), firstsOpt), lastsOpt)
You could probably also remove some of the noise by generalising <*> to work for any applicative.
I occasionally hit code like this:
val things : List[A \/ B] = ???
val (as, bs) : (List[A], List[B]) = ??? //insert something to do this
or in my current case I want Map[A, B \/ C] => (Map[A, B], Map[A, C])
Is there a nice way to do this in the general case F[A \/ B] with appropriate restrictions on F? It looks vaguely like a variation on the theme of Unzip.
Here's how we deal with this for / but also Either and Validation, and not just for Lists, but other Foldable.
object Uncozip {
implicit val wtf = language.higherKinds
// Typeclass which covers sum types such as \/, Either, Validation
trait Sum2[F[_, _]] {
def cata[A, B, X](a: A ⇒ X, b: B ⇒ X)(fab: F[A, B]): X
}
implicit val sumEither: Sum2[Either] = new Sum2[Either] {
def cata[A, B, X](a: A ⇒ X, b: B ⇒ X)(fab: Either[A, B]): X = {
fab match {
case Left(l) ⇒ a(l)
case Right(r) ⇒ b(r)
}
}
}
implicit val sumEitherz: Sum2[\/] = new Sum2[\/] {
def cata[A, B, X](a: A ⇒ X, b: B ⇒ X)(fab: A \/ B): X = {
fab.fold(a(_), b(_))
}
}
implicit val sumValidation: Sum2[Validation] = new Sum2[Validation] {
def cata[A, B, X](a: A ⇒ X, b: B ⇒ X)(fab: A Validation B): X = {
fab.fold(a(_), b(_))
}
}
abstract class Uncozips[F[_], G[_, _], A, B](fab: F[G[A, B]]) {
def uncozip: (F[A], F[B])
}
implicit def uncozip[F[_]: Foldable, G[_, _], A, B](fab: F[G[A, B]])(implicit g: Sum2[G], mfa: ApplicativePlus[F], mfb: ApplicativePlus[F]): Uncozips[F, G, A, B] = new Uncozips[F, G, A, B](fab) {
def uncozip = {
implicitly[Foldable[F]].foldRight[G[A, B], (F[A], F[B])](fab, (mfa.empty, mfb.empty)) { (l, r) ⇒
g.cata[A, B, (F[A], F[B])]({ (a: A) ⇒ (mfa.plus(mfa.point(a), r._1), r._2) },
{ (b: B) ⇒ (r._1, mfa.plus(mfa.point(b), r._2)) })(l)
}
}
}
}
You can map things in to a list of (Option[A], Option[B]), unzip that list in to two lists, and then unite the resulting lists:
import scalaz._
import Scalaz._
val things: List[String \/ Int] = List("foo".left, 42.right)
val (strs, ints): (List[String], List[Int]) = things.
map { d => (d.swap.toOption, d.toOption) }. // List[(Option[String], Option[Int])]
unzip. // (List[Option[String]], List[Option[Int]])
bimap(_.unite, _.unite) // (List[String], List[Int])
This isn't particularly efficient due to traversing the list three times.
Here is one way (for lists):
val things : List[A \/ B] = ???
val (as, bs) = (things.map(_.swap.toList).join, things.map(_.toList).join)
And for a map:
val things: Map[String, String \/ Int] = ???
val (as, bs) = (things.mapValues(_.swap.toList).filterNot(e => e._2.isEmpty),
things.mapValues(_.toList).filterNot(e => e._2.isEmpty))
I'm having a hard time coming up with a way to generalize this over any F (I believe you would need instances of Monoid and Applicative for F).
In Haskell, liftM2 can be defined as:
liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 f m1 m2 = do
x1 <- m1
x2 <- m2
return $ f x1 x2
I'd like to translate this to Scala. My first attempt was the following:
def liftM2[T1, T2, R, M[_]](f: (T1, T2) => R)(ma: M[T1], mb: M[T2]) : M[R] = for {
a <- ma
b <- mb
} yield f(a, b)
This fails in what I guess is the most obvious way possible: "value flatMap is not a member of type parameter M[T1]". Right, I haven't indicated that M[_] is some kind of monad. So the next thing I tried was to define some structural type like:
type Monad[A] = {
def flatMap[B](f: (A) => Monad[B]): Monad[B]
}
... and to have M[A] <: Monad[A]. But that doesn't work, because Scala doesn't have recursive structural types.
So the next few things I tried involved gyrations similar to M[A] <: FilterMonadic[A, _]. Those all failed, probably because I wasn't able to figure out the right implicit-fu for CanBuildFrom.
The most closely-related question I could find here on StackOverflow was this one, touching both on recursive structural types and how to mimic Haskell's typeclasses in Scala. But that approach requires defining an implicit conversion from each type you care about to the trait defining the typeclass, which seems terribly circular in this case...
Is there any good way to do what I'm trying to do?
The usual way to encode type classes in Scala turns out to follow Haskell pretty closely: List doesn't implement a Monad interface (as you might expect in an object-oriented language), but rather we define the type class instance in a separate object.
trait Monad[M[_]] {
def point[A](a: => A): M[A]
def bind[A, B](ma: M[A])(f: A => M[B]): M[B]
def map[A, B](ma: M[A])(f: A => B): M[B] = bind(ma)(a => point(f(a)))
}
implicit object listMonad extends Monad[List] {
def point[A](a: => A) = List(a)
def bind[A, B](ma: List[A])(f: A => List[B]) = ma flatMap f
}
This idea is introduced in Poor Man's Type Classes and explored more deeply in Type Classes as Objects and Implicits. Notice that the point method could not have been defined in an object-oriented interface, as it doesn't have M[A] as one of it's arguments to be converted to the this reference in an OO encoding. (Or put another way: it can't be part of an interface for the same reason a constructor signature can't be represented in an interface.)
You can then write liftM2 as:
def liftM2[M[_], A, B, C](f: (A, B) => C)
(implicit M: Monad[M]): (M[A], M[B]) => M[C] =
(ma, mb) => M.bind(ma)(a => M.map(mb)(b => f(a, b)))
val f = liftM2[List, Int, Int, Int](_ + _)
f(List(1, 2, 3), List(4, 5)) // List(5, 6, 6, 7, 7, 8)
This pattern has been applied extensively in Scalaz. Version 7, currently in development, includes an index of the type classes.
In addition to providing type classes and instances for standard library types, it provides a 'syntactic' layer that allows the more familiar receiver.method(args) style of method invocation. This often affords better type inference (accounting for Scala's left-to-right inference algorithm), and allows use of the for-comprehension syntactic sugar. Below, we use that to rewrite liftM2, based on the map and flatMap methods in MonadV.
// Before Scala 2.10
trait MonadV[M[_], A] {
def self: M[A]
implicit def M: Monad[M]
def flatMap[B](f: A => M[B]): M[B] = M.bind(self)(f)
def map[B](f: A => B): M[B] = M.map(self)(f)
}
implicit def ToMonadV[M[_], A](ma: M[A])
(implicit M0: Monad[M]) =
new MonadV[M, A] {
val M = M0
val self = ma
}
// Or, as of Scala 2.10
implicit class MonadOps[M[_], A](self: M[A])(implicit M: Monad[M]) {
def flatMap[B](f: A => M[B]): M[B] = M.flatMap(self)(f)
def map[B](f: A => B): M[B] = M.map(self)(f)
}
def liftM2[M[_]: Monad, A, B, C](f: (A, B) => C): (M[A], M[B]) => M[C] =
(ma, mb) => for {a <- ma; b <- mb} yield f(a, b)
Update
Yep, its possible to write less generic version of liftM2 for the Scala collections. You just have to feed in all the required CanBuildFrom instances.
scala> def liftM2[CC[X] <: TraversableLike[X, CC[X]], A, B, C]
| (f: (A, B) => C)
| (implicit ba: CanBuildFrom[CC[A], C, CC[C]], bb: CanBuildFrom[CC[B], C, CC[C]])
| : (CC[A], CC[B]) => CC[C] =
| (ca, cb) => ca.flatMap(a => cb.map(b => f(a, b)))
liftM2: [CC[X] <: scala.collection.TraversableLike[X,CC[X]], A, B, C](f: (A, B) => C)(implicit ba: scala.collection.generic.CanBuildFrom[CC[A],C,CC[C]], implicit bb: scala.collection.generic.CanBuildFrom[CC[B],C,CC[C]])(CC[A], CC[B]) => CC[C]
scala> liftM2[List, Int, Int, Int](_ + _)
res0: (List[Int], List[Int]) => List[Int] = <function2>
scala> res0(List(1, 2, 3), List(4, 5))
res1: List[Int] = List(5, 6, 6, 7, 7, 8)