Scala 平面映射上的FP-抽象
我的FP技能有点生疏了。您将如何调用以下构造,以及如何使Scala 平面映射上的FP-抽象,scala,functional-programming,monads,Scala,Functional Programming,Monads,我的FP技能有点生疏了。您将如何调用以下构造,以及如何使Seq[\u]替换为通用的G[\u](可能没有假定CanBuildFrom): 所以-我想在Top中更改为 def flatMap[A, B, G[_]](xs: F[A])(fun: A => G[B]): G[B] 最好的方法是什么?我应该去FP图书馆吗?我能用最小的猫或者更小的猫吗?这个术语是什么?如果我们采用以下定义: trait Top { type F[A] def map [A, B](xs: F[A]
Seq[\u]
替换为通用的G[\u]
(可能没有假定CanBuildFrom
):
所以-我想在Top
中更改为
def flatMap[A, B, G[_]](xs: F[A])(fun: A => G[B]): G[B]
最好的方法是什么?我应该去FP图书馆吗?我能用最小的猫或者更小的猫吗?这个术语是什么?如果我们采用以下定义:
trait Top {
type F[A]
def map [A, B](xs: F[A])(fun: A => B ): F [B]
def flatMap[A, B](xs: F[A])(fun: A => Seq[B]): Seq[B]
}
并将F
提升为顶部定义的更高类型:
trait Top[F[_]]
然后使用F
重新写入flatMap
:
trait Top[F[_]] {
def flatMap[A, B](xs: F[A])(fun: A => F[B]): F[B]
}
然后我们基本上得到了单子定义的前半部分。我们需要做的就是添加一种方法,将任何类型的a
提升到F[a]
的实例中:
trait Monad[F[_]] {
def pure[A](a: A): F[A]
def flatMap[A, B](xs: F[A])(fun: A => F[B]): F[B]
}
使用pure
和flatMap
(或return
和bind
,任意您喜欢的)我们可以免费获得map
。如果您决定使用其中一个函数库(如Cats),您将自动免费获得一些monad,这将自然地扩展Functor,并为任何monad实例获得map
如果你想从F
到G
,我们需要一个自然的转换。如果我们以猫为例,您可以为任何F
和G
定义一个这样的猫:
import cats.~>
val listToOptTransformer = new (List ~> Option) {
override def apply[A](fa: List[A]) = fa.headOption
}
如果你仔细想想,这是有道理的。如果您有一个F[A]
,那么在F
下生成任何类型的B
的唯一方法就是使用F.map
。但是F.map
根据定义只能产生F[B]
,而不是aG[B]
。因此,我们可以:
(flat)map NT
F[A] -> F[B] ~> G[B]
i、 e:
我们去:
F[A] F[B] G[B]
List[Int] -> List[String] -> Option[String]
经过进一步的挖掘,我的例子中的返回类型实际上一定略有不同。而不是
trait Top {
type F[_]
def foo[G[_], A, B](xs: F[A])(fun: A => G[B]): G[B]
}
一定是这样
trait Top {
type F[_]
def foo[G[_], A, B](xs: F[A])(fun: A => G[B]): G[F[B]]
}
在收到Gitter的scala/scala的一些反馈后,人们向我指出,需要的抽象是Traverse
。它需要一个Applicative
type类,然后它的工作方式如下:
trait Top {
type F[_]
def traverse[G[_]: Applicative, A, B](xs: F[A])(fun: A => G[B]): G[F[B]]
}
使用Id
和List
单子:
class IdExample extends Top {
type F[A] = A
def traverse[G[_]: Applicative, A, B](xs: F[A])(fun: A => G[B]): G[F[B]] = f(fa)
}
及
(这使用了Cats的接口,但是从foldRight
中删除了Eval
膨胀)我的问题是,我无法静态地知道F
和G
的具体类型,所以我想我无法导出静态已知的自然转换。因此,在封装方面,如果我在Top
上有一个类似于“半个变换”的辅助方法,对我来说会容易得多,在的意义上,可以从构建(但可能更简单),如果你理解我的意思的话。因此,调用者不需要知道F
@0\uuuuu。我们可以要求执行转换的方法从F~>G
进行隐式自然转换吗?也就是说,此方法的任何调用方都必须在作用域中进行这种转换?对我来说,这听起来与CanBuildFrom
相同。您还可以为它们提供最有用的现成转换,比如说Seq~>Option
等等。@0_uu例如:def convert[F:Monad,G:Monad](隐式fToG:F~>G)
Hmmm,我不这么认为:-(换句话说,F和G实际上是在不认识对方的情况下拼凑在一起的。@0_uuu哦,你实际上需要一个G[F[a]]
。我明白了,那么traverse
就很有意义了!很高兴它成功了。
trait Top {
type F[_]
def traverse[G[_]: Applicative, A, B](xs: F[A])(fun: A => G[B]): G[F[B]]
}
class IdExample extends Top {
type F[A] = A
def traverse[G[_]: Applicative, A, B](xs: F[A])(fun: A => G[B]): G[F[B]] = f(fa)
}
class ListExample extends Top {
type F[A] = List[A]
def traverse[G[_], A, B](xs: F[A])(fun: A => G[B])
(implicit app: Applicative[G]): G[F[B]] =
fa.foldRight(app.pure(List.empty)) { (a, lglb) =>
app.map2(f(a), lglb)(_ :: _)
}