scala - 仅为类标记参数实现函子映射
问题描述
我有以下数据结构:
class MyDaSt[A]{
def map[B: ClassTag](f: A => B) = //...
}
我想实现一个Functor
能够使用临时多态性的实例。明显的尝试如下:
implicit val mydastFunctor: Functor[MyDaSt] = new Functor[MyDaSt] {
override def map[A, B](fa: MyDaSt[A])(f: A => B): MyDaSt[B] = fa.map(f) //compile error
}
它显然无法编译,因为我们没有提供隐式ClassTag[B]
. 但是是否可以map
仅与f: A => B
具有ClassTag[B]
. 否则编译错误。我的意思是这样的:
def someFun[A, B, C[_]: Functor](cc: C[A], f: A => B) = cc.map(f)
val f: Int => Int = //...
val v: MyDaSt[Int] = //...
someFunc(v, f) //fine, ClassTag[Int] exists and in scope
无论如何我都无法更改它的实现,但我可以创建包装器(通过它看起来没有帮助)或继承。我可以自由使用shapeless
任何版本。
我目前认为在这种情况下无形是一种方法......
解决方案
我将扩展评论所触及的内容:
函子
cats.Functor
描述了Scala 类型类别中的 endofunctor - 也就是说,您应该能够使用并且必须支持任何 Scala 类型map
的函数。A => B
A
B
你所拥有的是一个数学函子,但在一个不同的、较小的类型中,具有ClassTag
. 这些通用函子有些不常见——我认为对于 stdlib 类型,只能SortedSet
是一类有序事物的函子——所以它现在在 Scala FP 中是相当未开发的领域,只是在 Scalaz 8 中有所传闻。
Cats 没有任何工具可以对这些东西进行抽象,因此您不会获得任何实用方法和生态系统支持。如果您想自己动手,可以使用@DmytroMitin 链接的答案
科米达
Coyoneda
可以从任何类型的构造函数对 Scala 类型创建一个内函子F[_]
。这个想法很简单:
- 有一些初始值
F[Initial]
- 有一个功能
Initial => A
- 到
map
withA => B
,你不会触及初始值,而只是简单地组合函数来获得Initial => B
您可以将任何内容提升F[A]
到cats.free.Coyoneda[F, A]
. 问题是如何F[A]
出去。
如果F
是 a cats.Functor
,那么你可以使用它是完全自然的 native map
,事实上,由于函子定律() ,使用Coyoneda
和直接使用不会有任何结果差异。F
x.map(f).map(g) <-> x.map(f andThen g)
在你的情况下,它不是。但是你可以cats.free.Coyoneda
拆开并委托给你自己的map
:
def coy[A](fa: MyDaSt[A]): Coyoneda[MyDaSt, A] = Coyoneda.lift(fa)
def unCoy[A: ClassTag](fa: Coyoneda[MyDaSt, A]): MyDaSt[A] =
fa.fi.map(fa.k) // fi is initial value, k is the composed function
这将让您使用期望的功能cats.Functor
:
def generic[F[_]: Functor, A: Show](fa: F[A]): F[String] = fa.map(_.show)
unCoy(generic(coy(v))) // ok, though cumbersome and needs -Ypartial-unification on scala prior to 2.13
(scastie 上的可运行示例)
一个明显的限制是你需要ClassTag[A]
在任何你想调用的地方都有一个unCo
- 即使你不需要它来创建一个实例MyDaSt[A]
。
不太明显的一点是,您不会自动保证没有行为差异。是否可以取决于您的map
操作 - 例如,如果它只是分配一些Array
s,它不应该引起问题。
推荐阅读
- android - 平板电脑上的 AlertDialog 中的空格顶部
- python - 给 Python 乌龟一个长方形的乒乓球游戏
- ios - AdditionalSafeAreaInsets 的奇怪行为
- java - 转换地图
来自 JSON - javascript - Javascript改变css(“宽度”)?
- java - requestLocationUpdates(), java.lang.RuntimeException - Android
- android - 如何将通用列表对象传递给其他 Activity 到 Xamarin Android 中的另一个 Activity?
- microsoft-graph-api - 通过 Graph API 向个人 Chat 发送消息
- node.js - 未调用 Mongoose 验证
- javascript - Reactjs 在使用谷歌地图时尝试缩放时返回未定义的地图