如何实现 Functor[数据集]



我正在努力如何创建Functor[Dataset]的实例...问题是,当您从AmapB时,Encoder[B]必须在隐式范围内,但我不确定该怎么做。

implicit val datasetFunctor: Functor[Dataset] = new Functor[Dataset] {
override def map[A, B](fa: Dataset[A])(f: A => B): Dataset[B] = fa.map(f)
}

当然,这段代码抛出了一个编译错误,因为Encoder[B]不可用,但我不能Encoder[B]添加为隐式参数,因为它会改变map方法签名,我该如何解决这个问题?

您无法立即申请f,因为您错过了Encoder。唯一明显的直接解决方案是:获取cats并重新实现所有接口,添加一个隐式Encoder参数。我看不到任何直接为Dataset实现Functor的方法。

但是,也许以下替代解决方案就足够了。 你可以做的是为数据集创建一个包装器,它有一个没有隐式Encodermap方法,但还有一个方法toDataset,它最终需要Encoder

对于这个包装器,你可以应用一个非常类似于所谓的Coyoneda构造(或Coyo?他们今天怎么称呼它?我不知道...它本质上是为任意类型构造函数实现"自由函子"的一种方式。

这是一个草图(它用猫 1.0.1 编译,用假人替换Spark特征):

import scala.language.higherKinds
import cats.Functor
/** Dummy for spark-Encoder */
trait Encoder[X]
/** Dummy for spark-Dataset */
trait Dataset[X] {
def map[Y](f: X => Y)(implicit enc: Encoder[Y]): Dataset[Y]
}
/** Coyoneda-esque wrapper for `Dataset` 
* that simply stashes all arguments to `map` away
* until a concrete `Encoder` is supplied during the
* application of `toDataset`.
*
* Essentially: the wrapped original dataset + concatenated
* list of functions which have been passed to `map`.
*/
abstract class MappedDataset[X] private () { self =>
type B
val base: Dataset[B]
val path: B => X
def toDataset(implicit enc: Encoder[X]): Dataset[X] = base map path
def map[Y](f: X => Y): MappedDataset[Y] = new MappedDataset[Y] {
type B = self.B
val base = self.base
val path: B => Y = f compose self.path
}
}
object MappedDataset {
/** Constructor for MappedDatasets.
* 
* Wraps a `Dataset` into a `MappedDataset` 
*/
def apply[X](ds: Dataset[X]): MappedDataset[X] = new MappedDataset[X] {
type B = X
val base = ds
val path = identity
}
}        
object MappedDatasetFunctor extends Functor[MappedDataset] {
/** Functorial `map` */
def map[A, B](da: MappedDataset[A])(f: A => B): MappedDataset[B] = da map f
}

现在,您可以将数据集ds包装到MappedDataset(ds)中,然后根据需要使用隐式MappedDatasetFunctor对其进行map,然后在最后调用toDataset,在那里您可以为最终结果提供具体的Encoder

请注意,这会将map中的所有函数合并到一个 Spark 阶段中:它将无法保存中间结果,因为缺少所有中间步骤的Encoder


我还没有完全学习cats,我不能保证这是最惯用的解决方案。图书馆里可能已经有一些Coyoneda式的东西。

编辑:猫库中有Coyoneda,但它需要自然转换F ~> G函子G。不幸的是,我们没有DatasetFunctor(这是首先的问题)。我上面的实现所做的是:它不需要Functor[G],而是在固定X(这就是Encoder[X])的(不存在的)自然变换的单一态射

相关内容

  • 没有找到相关文章

最新更新