So comonad.com has an interesting series of articles on working with applicatives and I've been trying to bring what I can to scala (for fun, and to learn). So, haskell defines FixF –
newtype FixF f a = FixF (f (FixF f) a)
it is written ,"FixF is of kind ((* -> *) -> * -> *) -> * -> *). It takes the fixpoint of a "second-order Functor" (a Functor that sends a Functor to another Functor, i.e. an endofunctor on the functor category of hask), to recover a standard "first order Functor" back out."
kinds classify types
* type A
* -> * type F[_]
* -> * -> * type F[_,_]
((* -> *) -> *) type F[F'[_]]
((* -> *) ->* -> *) type F[F'[_], A]
Now I've seen this
case class Fix[F[_]](out: F[Fix[F]])
// ((* -> *) -> * )
and this
case class BFixF[F[_,_], A](out: F[A, BFixF[F,A]])
So it's not the first one Fix (wrong kinds) Is it the second? I don't the think the kinds are right
BFixF :: ((* -> * -> * ) -> * -> *) ?
is it -
// edit as of this morning it is really not this
class FixF[F[_[_], _], A] :: ((* -> *) -> * -> *) -> *)
is it ?
case class FixF'[F[_], A](run: F[Fix[F, A]])
if so I'd love to see the proper definition and functor
case class FixF[F[_], A] (out: F[Fix[F, A]])
trait FixFFunctor[F[_]: Functor] extends Functor[({type l[x] = FixF[F, x]})#l] {
def map[A, B](f: A => B): FixF[F, A] => FixF[F, B] = ???
}
now bonus question, can someone define the applicative ?
This is a pretty cool question—I'd also read those posts, and had wondered about how terrifying a Scala implementation would look, but I never actually tried it. So I'm going to respond in some detail, but please note that the following is extremely off-the-cuff (it's Saturday morning, after all) and doesn't necessarily represent the cleanest way to do this in Scala.
It's probably best to start by defining some of the types from the first post in the series:
import scala.language.higherKinds
import scalaz._, Scalaz._
case class Const[M, A](mo: M)
sealed trait Sum[F[_], G[_], A]
object Sum {
def inL[F[_], G[_], A](l: F[A]): Sum[F, G, A] = InL(l)
def inR[F[_], G[_], A](r: G[A]): Sum[F, G, A] = InR(r)
}
case class InL[F[_], G[_], A](l: F[A]) extends Sum[F, G, A]
case class InR[F[_], G[_], A](r: G[A]) extends Sum[F, G, A]
And a couple more from the blog post itself:
case class Embed[F[_], A](out: A)
case class ProductF[F[_[_], _], G[_[_], _], B[_], A](f: F[B, A], g: G[B, A])
If you've worked through the above, you should have some sense of what FixF should look like:
case class FixF[F[f[_], _], A](out: F[({ type L[x] = FixF[F, x] })#L, A])
It turns out that this is a little too strict, though, so we'll use the following instead:
class FixF[F[f[_], _], A](v: => F[({ type L[x] = FixF[F, x] })#L, A]) {
lazy val out = v
override def toString = s"FixF($out)"
}
Now suppose we want to implement lists as a "second-order fixpoint of polynomial functors", as in the blog post. We can start by defining some useful aliases:
type UnitConst[x] = Const[Unit, x]
type UnitConstOr[F[_], x] = Sum[UnitConst, F, x]
type EmbedXUnitConstOr[F[_], x] = ProductF[Embed, UnitConstOr, F, x]
type MyList[x] = FixF[EmbedXUnitConstOr, x]
And now we can define the Scala version of the examples from the post:
val foo: MyList[String] = new FixF[EmbedXUnitConstOr, String](
ProductF[Embed, UnitConstOr, MyList, String](
Embed("foo"),
Sum.inL[UnitConst, MyList, String](Const())
)
)
val baz: MyList[String] = new FixF[EmbedXUnitConstOr, String](
ProductF[Embed, UnitConstOr, MyList, String](
Embed("baz"),
Sum.inL[UnitConst, MyList, String](Const())
)
)
val bar: MyList[String] = new FixF[EmbedXUnitConstOr, String](
ProductF[Embed, UnitConstOr, MyList, String](
Embed("bar"),
Sum.inR[UnitConst, MyList, String](baz)
)
)
This looks like what we'd expect given the Haskell implementation:
scala> println(foo)
FixF(ProductF(Embed(foo),InL(Const(()))))
scala> println(bar)
FixF(ProductF(Embed(bar),InR(FixF(ProductF(Embed(baz),InL(Const(())))))))
Now we need our type class instances. Most of these are pretty straightforward:
implicit def applicativeConst[M: Monoid]: Applicative[
({ type L[x] = Const[M, x] })#L
] = new Applicative[({ type L[x] = Const[M, x] })#L] {
def point[A](a: => A): Const[M, A] = Const(mzero[M])
def ap[A, B](fa: => Const[M, A])(f: => Const[M, A => B]): Const[M, B] =
Const(f.mo |+| fa.mo)
}
implicit def applicativeEmbed[F[_]]: Applicative[
({ type L[x] = Embed[F, x] })#L
] = new Applicative[({ type L[x] = Embed[F, x] })#L] {
def point[A](a: => A): Embed[F, A] = Embed(a)
def ap[A, B](fa: => Embed[F, A])(f: => Embed[F, A => B]): Embed[F, B] =
Embed(f.out(fa.out))
}
implicit def applicativeProductF[F[_[_], _], G[_[_], _], B[_]](implicit
fba: Applicative[({ type L[x] = F[B, x] })#L],
gba: Applicative[({ type L[x] = G[B, x] })#L]
): Applicative[({ type L[x] = ProductF[F, G, B, x] })#L] =
new Applicative[({ type L[x] = ProductF[F, G, B, x] })#L] {
def point[A](a: => A): ProductF[F, G, B, A] =
ProductF(fba.point(a), gba.point(a))
def ap[A, C](fa: => ProductF[F, G, B, A])(
f: => ProductF[F, G, B, A => C]
): ProductF[F, G, B, C] = ProductF(fba.ap(fa.f)(f.f), gba.ap(fa.g)(f.g))
}
Including the applicative instance for FixF itself:
implicit def applicativeFixF[F[_[_], _]](implicit
ffa: Applicative[({ type L[x] = F[({ type M[y] = FixF[F, y] })#M, x] })#L]
): Applicative[({ type L[x] = FixF[F, x] })#L] =
new Applicative[({ type L[x] = FixF[F, x] })#L] {
def point[A](a: => A): FixF[F, A] = new FixF(ffa.pure(a))
def ap[A, B](fa: => FixF[F, A])(f: => FixF[F, A => B]): FixF[F, B] =
new FixF(ffa.ap(fa.out)(f.out))
}
We'll also define the terminal transformation:
implicit def terminal[F[_], M: Monoid]: F ~> ({ type L[x] = Const[M, x] })#L =
new (F ~> ({ type L[x] = Const[M, x] })#L) {
def apply[A](fa: F[A]): Const[M, A] = Const(mzero[M])
}
But now we're in trouble. We really need some extra laziness in here, so we'll cheat a little:
def applicativeSum[F[_], G[_]](
fa: Applicative[F],
ga: => Applicative[G],
nt: G ~> F
): Applicative[({ type L[x] = Sum[F, G, x] })#L] =
new Applicative[({ type L[x] = Sum[F, G, x] })#L] {
def point[A](a: => A): Sum[F, G, A] = InR(ga.point(a))
def ap[A, B](x: => Sum[F, G, A])(f: => Sum[F, G, A => B]): Sum[F, G, B] =
(x, f) match {
case (InL(v), InL(f)) => InL(fa.ap(v)(f))
case (InR(v), InR(f)) => InR(ga.ap(v)(f))
case (InR(v), InL(f)) => InL(fa.ap(nt(v))(f))
case (InL(v), InR(f)) => InL(fa.ap(v)(nt(f)))
}
}
implicit def myListApplicative: Applicative[MyList] =
applicativeFixF[EmbedXUnitConstOr](
applicativeProductF[Embed, UnitConstOr, MyList](
applicativeEmbed[MyList],
applicativeSum[UnitConst, MyList](
applicativeConst[Unit],
myListApplicative,
terminal[MyList, Unit]
)
)
)
Maybe there's a way to get this to work with Scalaz 7's encoding of applicatives without the hack, but if there is I don't want to spend my Saturday afternoon figuring it out.
So that sucks, but at least now we can check our work:
scala> println((foo |#| bar)(_ ++ _))
FixF(ProductF(Embed(foobar),InL(Const(()))))
Which is exactly what we wanted.
Just by looking at the Haskell data type, I'd try the following class:
import scala.language.higherKinds
class FixF[F[_,_],A](val ffix: F[FixF[F,_],A]) extends AnyVal;
I'll try to expand the answer later when I learn more about FixF.
Related
Trying to grasp Scala 3 type system.
Question:
Is it possible to write a single universal def curry(f: ???) = ... function that accepts f of any arity and returns a curried fn? No compiler plugins, no external fancy libs, just a function of N-arity expressed in plain Scala 3?
I look at this Haskell example https://riptutorial.com/haskell/example/18470/an-n-arity-curry that does smth similar to what is needed.
(purpose of this question is not to use any external lib - purpose is to learn functional programming concepts with Scala 3 as a tool. Got a feeling that this might be related to dealing with args as tuples or some conversion of fn to tupled fn ? i feel there is some symmetry between fn args and a concept of tuple ?)
On contrary to Haskell, in Scala there are different functional types (X1, ..., Xn) => Y (aka FunctionN[X1, ..., Xn, Y]) and ((X1, ..., Xn)) => Y (aka Function1[TupleN[X1, ..., Xn], Y]). For the latter (in order to transform them into X1 => ... => Xn => Y aka Function1[X1, Function1[..., Function1[Xn, Y]...]]) you can use match types, inline methods, and compile-time operations
import scala.compiletime.{erasedValue, summonFrom}
type Reverse[T <: Tuple] = ReverseLoop[T, EmptyTuple]
inline def reverse[T <: Tuple](t: T): Reverse[T] = reverseLoop(t, EmptyTuple)
type ReverseLoop[T <: Tuple, S <: Tuple] <: Tuple = T match
case EmptyTuple => S
case h *: t => ReverseLoop[t, h *: S]
inline def reverseLoop[T <: Tuple, S <: Tuple](x: T, acc: S): ReverseLoop[T, S] =
inline x match
case _: EmptyTuple => acc
case x: (_ *: _) => x match
case h *: t => reverseLoop(t, h *: acc)
type Curry[T <: Tuple, Y] = CurryLoop[T, T, EmptyTuple, Y]
inline def curry[T <: Tuple, Y](f: T => Y): Curry[T, Y] =
curryLoop[T, T, EmptyTuple, Y](f, EmptyTuple)
type CurryLoop[T1 <: Tuple, T <: Tuple, S <: Tuple, Y] = T1 match
case EmptyTuple => Y
case h *: t => h => CurryLoop[t, T, h *: S, Y]
inline def curryLoop[T1 <: Tuple, T <: Tuple, S <: Tuple, Y](
f: T => Y,
acc: S
): CurryLoop[T1, T, S, Y] = inline erasedValue[T1] match
case _: EmptyTuple => summonFrom {
case _: (Reverse[S] =:= T) => f(reverse(acc))
}
case _: (h *: t) => (h: h) => curryLoop[t, T, h *: S, Y](f, h *: acc)
Testing:
// compiles
summon[Curry[(Int, String, Boolean), String] =:= (Int => String => Boolean => String)]
val f: ((Int, String, Boolean)) => String = t => s"${t._1}, ${t._2}, ${t._3}"
val g = curry(f)
g: (Int => String => Boolean => String) // checking the type
g(1)("a")(true) // 1, a, true
Scala 3: typed tuple zipping
Alternatively, you can still use good old type classes
trait Reverse[T <: Tuple]:
type Out <: Tuple
def apply(t: T): Out
object Reverse:
type Aux[T <: Tuple, Out0 <: Tuple] = Reverse[T] {type Out = Out0}
def instance[T <: Tuple, Out0 <: Tuple](f: T => Out0): Aux[T, Out0] =
new Reverse[T]:
override type Out = Out0
override def apply(t: T): Out = f(t)
given [T <: Tuple](using
reverseLoop: ReverseLoop[T, EmptyTuple]
): Aux[T, reverseLoop.Out] = instance(t => reverseLoop(t, EmptyTuple))
trait ReverseLoop[T <: Tuple, S <: Tuple]:
type Out <: Tuple
def apply(t: T, acc: S): Out
object ReverseLoop:
type Aux[T <: Tuple, S <: Tuple, Out0 <: Tuple] =
ReverseLoop[T, S] {type Out = Out0}
def instance[T <: Tuple, S <: Tuple, Out0 <: Tuple](
f: (T, S) => Out0
): Aux[T, S, Out0] = new ReverseLoop[T, S]:
override type Out = Out0
override def apply(t: T, acc: S): Out = f(t, acc)
given [S <: Tuple]: Aux[EmptyTuple, S, S] = instance((_, acc) => acc)
given [H, T <: Tuple, S <: Tuple](using
reverseLoop: ReverseLoop[T, H *: S]
): Aux[H *: T, S, reverseLoop.Out] =
instance((l, acc) => reverseLoop(l.tail, l.head *: acc))
trait Curry[T <: Tuple, Y]:
type Out
def apply(f: T => Y): Out
object Curry:
type Aux[T <: Tuple, Y, Out0] = Curry[T, Y] {type Out = Out0}
def instance[T <: Tuple, Y, Out0](g: (T => Y) => Out0): Aux[T, Y, Out0] =
new Curry[T, Y]:
override type Out = Out0
override def apply(f: T => Y): Out = g(f)
given [T <: Tuple, Y](using
curryLoop: CurryLoop[T, T, EmptyTuple, Y]
): Aux[T, Y, curryLoop.Out] = instance(f => curryLoop(f, EmptyTuple))
trait CurryLoop[T1 <: Tuple, T <: Tuple, S <: Tuple, Y]:
type Out
def apply(f: T => Y, acc: S): Out
object CurryLoop:
type Aux[T1 <: Tuple, T <: Tuple, S <: Tuple, Y, Out0] =
CurryLoop[T1, T, S, Y] {type Out = Out0}
def instance[T1 <: Tuple, T <: Tuple, S <: Tuple, Y, Out0](
g: (T => Y, S) => Out0
): Aux[T1, T, S, Y, Out0] = new CurryLoop[T1, T, S, Y]:
override type Out = Out0
override def apply(f: T => Y, acc: S): Out = g(f, acc)
given [S <: Tuple, Y](using
reverse: Reverse[S]
): Aux[EmptyTuple, reverse.Out, S, Y, Y] =
instance((f, acc) => f(reverse(acc)))
given [H1, T1 <: Tuple, T <: Tuple, S <: Tuple, Y](using
curryLoop: CurryLoop[T1, T, H1 *: S, Y]
): Aux[H1 *: T1, T, S, Y, H1 => curryLoop.Out] =
instance((f, acc) => h1 => curryLoop(f, h1 *: acc))
def curry[T <: Tuple, Y](f: T => Y)(using
curryInst: Curry[T, Y]
): curryInst.Out = curryInst(f)
Testing:
// compiles
summon[Curry.Aux[(Int, String, Boolean), String, Int => String => Boolean => String]]
val c = summon[Curry[(Int, String, Boolean), String]] // compiles
summon[c.Out =:= (Int => String => Boolean => String)] // compiles
val f: ((Int, String, Boolean)) => String = t => s"${t._1}, ${t._2}, ${t._3}"
val g = curry(f)
g: (Int => String => Boolean => String) // checking the type
g(1)("a")(true) // 1, a, true
A method tupled transforming (X1, ..., Xn) => Y into ((X1, ..., Xn)) => Y can be implemented as a transparent macro. A macro being transparent (this corresponds to whitebox in Scala 2) means that it can return a type more precise than declared.
import scala.quoted.*
transparent inline def tupled[F](f: F): Any = ${tupledImpl('f)}
def tupledImpl[F: Type](f: Expr[F])(using Quotes): Expr[Any] =
import quotes.reflect.*
val allTypeArgs = TypeRepr.of[F].typeArgs
val argTypes = allTypeArgs.init
val argCount = argTypes.length
val returnType = allTypeArgs.last
val tupleType = AppliedType(
TypeRepr.typeConstructorOf(Class.forName(s"scala.Tuple$argCount")),
argTypes
)
(tupleType.asType, returnType.asType) match
case ('[t], '[b]) => '{
(a: t) => ${
Apply(
Select.unique(f.asTerm, "apply"),
(1 to argCount).toList.map(i => Select.unique('a.asTerm, s"_$i"))
).asExprOf[b]
}
}
Testing:
val f: (Int, String, Boolean) => String = (i, s, b) => s"$i, $s, $b"
val g = tupled(f)
g: (((Int, String, Boolean)) => String) // checking the type
g((1, "a", true)) // 1, a, true
This gives us curry for types (X1, ..., Xn) => Y
curry(tupled(f))(1)("a")(true) // 1, a, true
Although curry(tupled(f)) works for a specific f it's not easy to specify the signature of a method (composing curry and tupled)
// for match-type implementation of curry
transparent inline def curry1[F](f: F): Any = curry(tupled(f))
curry1(f)(1)("a")(true)
// doesn't compile: method curry1 ... does not take more parameters
// for type-class implementation of curry
transparent inline def curry1[F](f: F): Any = curry(tupled(f))
// doesn't compile: No given instance of type Curry[Nothing, Any] was found...
// (and what types to specify in (using Curry[???, ???]) ?)
I thought that Recovering precise types using patterns should help if I make curry1 a macro too
transparent inline def curry1[F](f: F): Any = ${curry1Impl[F]('f)}
def curry1Impl[F: Type](f: Expr[F])(using Quotes): Expr[Any] =
import quotes.reflect.*
'{ tupled[F]($f) } match
case
'{
type t <: Tuple
$x: (`t` => y)
} =>
Expr.summon[Curry[t, y]] match
case Some(c) => '{curry[t, y]($x)(using $c)}
but it doesn't. If transparent inline def tupled[F](f: F): Any = ... then '{ tupled[F]($f) } doesn't match '{...; $x: (`t` => y)}. If transparent inline def tupled[F](f: F): Function1[?, ?] = ... then t is Nothing, y is Any.
So let's make tupled an implicit macro (type class) in order to control better the return type of tupled
import scala.quoted.*
trait Tupled[F]:
type Out
def apply(f: F): Out
object Tupled:
type Aux[F, Out0] = Tupled[F] { type Out = Out0 }
def instance[F, Out0](g: F => Out0): Aux[F, Out0] = new Tupled[F]:
type Out = Out0
def apply(f: F): Out = g(f)
transparent inline given [F]: Tupled[F] = ${mkTupledImpl[F]}
def mkTupledImpl[F: Type](using Quotes): Expr[Tupled[F]] =
import quotes.reflect.*
val allTypeArgs = TypeRepr.of[F].typeArgs
val argTypes = allTypeArgs.init
val argCount = argTypes.length
val returnType = allTypeArgs.last
val tupleType = AppliedType(
TypeRepr.typeConstructorOf(Class.forName(s"scala.Tuple$argCount")),
argTypes
)
(tupleType.asType, returnType.asType) match
case ('[t], '[b]) => '{
instance[F, t => b]((f: F) => (a: t) => ${
Apply(
Select.unique('f.asTerm, "apply"),
(1 to argCount).toList.map(i => Select.unique('a.asTerm, s"_$i"))
).asExprOf[b]
})
}
def tupled[F](f: F)(using tupledInst: Tupled[F]): tupledInst.Out = tupledInst(f)
// for match-type implementation of curry
inline def curry1[F, T <: Tuple, Y](f: F)(using
tupledInst: Tupled[F],
ev: tupledInst.Out <:< (T => Y),
): Curry[T, Y] = curry(tupled(f))
Testing:
val f: (Int, String, Boolean) => String = (i, s, b) => s"$i, $s, $b"
val g = curry1(f)
g : (Int => String => Boolean => String) // checking the type
g(1)("a")(true) // 1, a, true
Alternatively to tupled, you can try built-in type class scala.util.TupledFunction https://docs.scala-lang.org/scala3/reference/experimental/tupled-function.html (thanks to #MartinHH
for pointing this out in the comments)
// for match-type implementation of curry
inline def curry1[F, T <: Tuple, Y](f: F)(using
tf: TupledFunction[F, T => Y]
): Curry[T, Y] = curry(tf.tupled(f))
// for type-class implementation of curry
def curry1[F, T <: Tuple, Y](f: F)(using
tf: TupledFunction[F, T => Y],
c: Curry[T, Y]
): c.Out = curry(tf.tupled(f))
TupledFunction is similar to type classes shapeless.ops.function.{FnToProduct, FnFromProduct} in Scala 2
https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#facilities-for-abstracting-over-arity
Partial function application in Scala for arbitrary input arguments
Function taking another function of arbitrary arity as argument
Scala's type system and the input to FunctionN
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.)
def isEven(x: Int) =
if (x % 2 == 0) x.successNel else "not even".failureNel
In the above code, the return type of isEven is inferred as scalaz.Validation[scalaz.NonEmptyList[_ <: String],Int]
Whereas explicitly specifying the return type works,
def isEven(x: Int): ValidationNel[String, Int] =
if (x % 2 == 0) x.successNel else "not even".failureNel
Can someone explain what's going on behind the scenes here? Is this a limitation of Scala's type inference system?
I'm trying to use the above function in the following expression,
(isEven(4) |#| isEven(6) |#| isEven(8) |#| isEven(10)) {_ + _ + _ + _}
and only the second variant (with explicit return type) works.
scalaz.NonEmptyList[String] is a sub type of scalaz.NonEmptyList[_ <: String], meaning scalaz.ValidationNEL[String,Int] is a sub type of scalaz.Validation[scalaz.NonEmptyList[_ <: String],Int]. The compiler just give you a more precise type in this case...
Edit: the raisin bread operator requires an Applicative instance. In this case I suppose that scalaz provide such instance for ValidationNel[String, ?], but not for the more precise type inferred for your isEven.
A simpler manifestation of that can be observed with Option:
val some = Some(1)
val none = None
(some |#| none)(_ + _) // Cannot find implicit Applicative[Some] :(
You can view that as an intersect limitation of local type inference. You could also say that FP doesn't always play well with OO. I would suggest following good practices and annotate all your public method and implicits with explicit types.
I will try to explain more details about :
why scalaz.Validation[scalaz.NonEmptyList[_ <: String],Int] this can not work with |#|?
isEven(4) |#| isEven(6) ...
First of all isEven(4) is trying to implicitly convert it from Validation[+E, +A] type to ApplyOps[F0.M,F0.A]type with |#| operator. as the implicit method in ApplySyntax:
implicit def ToApplyOpsUnapply[FA](v: FA)(implicit F0: Unapply[Apply, FA]) =
new ApplyOps[F0.M,F0.A](F0(v))(F0.TC)
As the above code, for this implicit conversion, we also need an implicitly F0: Unapply[Apply, FA] variable. for UnApply implicits, it's in the UnApply:
implicit def unapplyMFA[TC[_[_]], M0[_[_], _], F0[_], A0](implicit TC0: TC[M0[F0, ?]]): Unapply[TC, M0[F0, A0]] {
type M[X] = M0[F0, X]
type A = A0
} =
new Unapply[TC, M0[F0, A0]] {
type M[X] = M0[F0, X]
type A = A0
def TC = TC0
def leibniz = refl
}
As the Validation type, it's using the unapplyMFA implicits variable. In there we also found it's looking for another implicits TC0: TC[M0[F0, ?]] variable. as the before ToApplyOpsUnapply, In there TC0 will be Apply type, that's also can be Applicative type. so it will try to look for the Validation to Applicative implicits, it's in the Validation.scala
implicit def ValidationApplicative[L: Semigroup]: Applicative[Validation[L, ?]] =
new Applicative[Validation[L, ?]] {
override def map[A, B](fa: Validation[L, A])(f: A => B) =
fa map f
def point[A](a: => A) =
Success(a)
def ap[A, B](fa: => Validation[L, A])(f: => Validation[L, A => B]) =
fa ap f
}
for the Semigroup definition:
trait Semigroup[F] { self => //F is invariant, unlike Option[+A]
So the problem is: F is type invariant, not like the Option[+A], compiler could not find an appropriate Applicative implicits for this type Validaiton.
There is a small demo for how to enable type variant for this:
def isEven(x: Int): Validation[NonEmptyList[_ <: String], Int] =
if (x % 2 == 0) x.successNel else "not even".failureNel
val res: String = foo(isEven(4))
def foo[FA](v: FA)(implicit F0: Unapply[Apply, FA]): String = "foo bar"
implicit def ValidationApplicative[L]: Applicative[Validation[L, ?]] =
new Applicative[Validation[L, ?]] {
override def map[A, B](fa: Validation[L, A])(f: A => B) =
fa map f
def point[A](a: => A) =
Success(a)
def ap[A, B](fa: => Validation[L, A])(f: => Validation[L, A => B]) =
//fa ap f
null
}
In there we just unbind L from Semigroup, so now this is type variant. just for fun;).
So I've been trying to push my intuitions of functors to their limits by defining a higher order functor i.e. a, F that takes 1st order types as type argument, and functions and lifts functions on 1st order types to this higher context in scala something like
trait Functor1[F[_[_]] {
def hmap[X[_], Y[_]] : (X ~> Y) => F[X] => F[Y]
}
I've been trying to define some of the map derivable functions of the normal functor e.g.
trait Functor[F[_]] {
def map[A, B] : (A => B) => F[A] => F[B]
// there's a few more besides this that are map derivable
def distribute[A,B](fab: F[(A, B)]): (F[A], F[B])
}
but I can't write anything that type checks...
I'm just playing but I wonder if anyone else has been down this road that's smarter than me
can a higher order functor be defined in scala ? if not then in haskell ?
Not sure what are your goals, but this typechecks
import scala.language.higherKinds
trait Functor1[F[G[_]]]{
def hmap[X[_], Y[_]]:(X ~> Y) => F[X] => F[Y]
}
case class FId[Z,F[_]](f:F[Z])
implicit def Functor1Id[Z] = new Functor1[({type L[G[_]]=FId[Z,G]})#L]{
def hmap[X[_], Y[_]]:(X ~> Y) => FId[Z,X] => FId[Z,Y]= ???
}
(I added the Z parameter because I wanted to avoid existentials and I had to use the "type lambda" trick)
Do you want to define a map for a "functor of a functor"?
I think I did something similar (here called composition):
case class Comp[F[_],G[_],Z](unComp:F[G[Z]])
implicit def fcomp[F[_], G[_]](implicit ff:Functor[F], fg:Functor[G])=new Functor[({ type abs[A]=Comp[F,G,A]})#abs]{
def fmap[A,B](fga:Comp[F,G,A])(f: A => B):Comp[F,G,B]= Comp(ff.fmap(fga.unComp)(fg.fmap(_)(f)))
}
I've been toying with functors in scala-reggen but I don't think I'm the smart one, as I mainly did it by fumbling around (and checking Scalaz for inspiration)
/** Higher order functor */
trait HFunctor[F[_]] {
def ffmap[G[_]: Functor, A, B](f: A => B): F[G[A]] => F[G[B]]
def hfmap[G[_], H[_]](t: G ~> H): ({type λ[α] = F[G[α]]})#λ ~> ({type λ[α] = F[H[α]]})#λ
}
trait Functor[F[_]] { self =>
def fmap[A, B](f: A => B): F[A] => F[B]
// derived
def map[A, B](x: F[A])(f: A => B): F[B] = fmap(f)(x)
def strengthL[A, B]: A => F[B] => F[(A, B)] = a => f => fmap((x: B) => (a, x))(f)
def strengthR[A, B]: F[A] => B => F[(A, B)] = f => b => fmap((x: A) => (x, b))(f)
def compose[G[_]](implicit e: Functor[G]): Functor[({ type λ[α] = F[G[α]]})#λ] =
new Functor[({ type λ[α] = F[G[α]]})#λ] {
def F = self;
def G = e
def fmap[A, B](f: A => B) = F.fmap(G.fmap(f))
}
}
object Functor {
#inline def apply[F[_]: Functor]: Functor[F] = iev
}
trait Coyoneda[F[_], A] { co =>
type I
def fi: F[I]
def k: I => A
final def run(implicit F: Functor[F]): F[A] = F.fmap(k)(fi)
final def map[B](f: A => B): Coyoneda.Aux[F, B, I] =
Coyoneda(fi)(f compose k)
final def trans[G[_]](phi: F ~> G): Coyoneda[G, A] =
Coyoneda(phi(fi))(k)
}
object Coyoneda {
type Aux[F[_], A, B] = Coyoneda[F, A] { type I = B }
def apply[F[_], B, A](x: F[B])(f: B => A): Aux[F, A, B] =
new Coyoneda[F, A] {
type I = B
val fi = x
val k = f
}
implicit def coyonedaFunctor[F[_]]: Functor[({ type λ[α] = Coyoneda[F, α] })#λ] =
new Functor[({ type λ[α] = Coyoneda[F, α] })#λ] {
def fmap[A, B](f: A => B): Coyoneda[F, A] => Coyoneda[F, B] =
x => apply(x.fi)(f compose x.k)
}
implicit def coyonedaHFunctor: HFunctor[({ type λ[F[_]] = ({ type λ[α] = Coyoneda[F, α] })#λ })#λ] =
new HFunctor[({ type λ[F[_]] = ({ type λ[α] = Coyoneda[F, α] })#λ })#λ] {
def ffmap[G[_]: Functor, A, B](f: A => B): Coyoneda[G, A] => Coyoneda[G, B] = _.map(f)
def hfmap[F[_], G[_]](t: F ~> G): (({ type λ[α] = Coyoneda[F, α] })#λ ~> ({ type λ[α] = Coyoneda[G, α] })#λ) =
new (({ type λ[α] = Coyoneda[F, α] })#λ ~> ({ type λ[α] = Coyoneda[G, α] })#λ) {
def apply[A](x: Coyoneda[F, A]) = x.trans(t)
}
}
def liftCoyoneda[F[_], A](fa: F[A]): Coyoneda[F, A] = apply(fa)(x => x)
def lowerCoyoneda[F[_]: Functor, A](c: Coyoneda[F, A]): F[A] = c.run
}
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).