To jest całkiem fajne pytanie. Przeczytałem również te posty i zastanawiałem się, jak przerażające będzie wyglądanie w Scali, ale nigdy tego nie wypróbowałem. Tak więc zamierzam odpowiedzieć w szczegółach, ale proszę zauważyć, że poniższe są wyjątkowo niegotowe (w końcu jest sobotni poranek) i niekoniecznie przedstawiają najczystszy sposób na zrobienie tego w Scali.
To chyba najlepiej zacząć od zdefiniowania niektórych typów z 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]
i kilka więcej od 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])
Jeśli pracował przez wyżej, powinieneś poczuć, jak powinien wyglądać FixF
:
case class FixF[F[f[_], _], A](out: F[({ type L[x] = FixF[F, x] })#L, A])
Okazuje się, że jest to trochę zbyt surowe, choć, więc użyjemy następujące zamiast:
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)"
}
Załóżmy teraz chcemy wdrożyć list jako „fixpoint drugiego rzędu wielomianu funktorów” jak na blogu. Możemy zacząć od zdefiniowania kilka przydatnych aliasów:
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]
A teraz możemy zdefiniować wersję Scala z przykładów ze stanowiska:
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)
)
)
To wygląda to, czego oczekujemy, biorąc pod uwagę wdrożenie Haskell :
scala> println(foo)
FixF(ProductF(Embed(foo),InL(Const(()))))
scala> println(bar)
FixF(ProductF(Embed(bar),InR(FixF(ProductF(Embed(baz),InL(Const(())))))))
Teraz potrzebujemy naszych instancji klasy typów. Większość z nich jest całkiem prosta:
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))
}
Włączając aplikacyjnej przykład dla FixF
sobie:
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))
}
Będziemy także zdefiniować transformację zacisk:
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])
}
Ale teraz jesteśmy w tarapatach. Naprawdę potrzebujesz dodatkowej lenistwo tu, więc będziemy oszukiwać trochę:
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]
)
)
)
Może istnieje sposób, aby uzyskać to do pracy z kodowaniem applicatives Scalaz 7 bez hack, ale jeśli nie ma I don” Chcę spędzić sobotnie popołudnie.
Tak, że jest do bani, ale przynajmniej teraz możemy sprawdzić naszą pracę:
scala> println((foo |@| bar)(_ ++ _))
FixF(ProductF(Embed(foobar),InL(Const(()))))
który jest dokładnie to, co chcieliśmy.
Czy należy to oznaczyć [python]? Nie rozumiem dlaczego? –
@jb, nie ma powodu, aby oznaczyć to pytanie python. ponieważ oczywiście nie ma nic do pytona. – DEAD