Scala 平面映射上的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]

我的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])(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]
,而不是a
G[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)(_ :: _)
    }