scala – 通过最短的路线链接
问题
我们之间有一组类型和转换.这听起来像DAG,并有一些相似之处.如果可行,我希望能够计算任何两种类型之间的隐式最短转换路径. 我已经准备了简单的例子,显示我无意中宣布这样的暗示. final case class A(u : Int) final case class B(u : Int) final case class BB(u : Int) final case class C(u : Int) final case class D(u: Int) trait Convert[F,T] { def convert(source : F) : T } 我介绍以下测试用例转换:A – > B,A – > BB,B – > C,B→> D,C – > D. 我尝试了两种方法,并给出了不同的隐式分辨率错误. 过境链 trait ConcreteConvert[F,T] extends Convert[F,T] class Transit[F,M,T](implicit fm : ConcreteConvert[F,M],mt : Convert[M,T]) extends Convert[F,T] { override def convert(source : F) : T = mt.convert( fm.convert(source) ) } object Implicits { implicit def transit[F,T]) : Convert[F,T] = new Transit()(fm,mt) implicit object A2B extends ConcreteConvert[A,B] { override def convert(source : A) : B = B(source.u) } implicit object B2C extends ConcreteConvert[B,C] { override def convert(source : B) : C = C(source.u) } /*implicit object A2BB extends ConcreteConvert[A,BB] { override def convert(source : A) : BB = BB(source.u) }*/ // compilation fails implicit object C2D extends ConcreteConvert[C,D] { override def convert(source : C) : D = D(source.u) } implicit object B2D extends ConcreteConvert[B,D] { override def convert(source : B) : D = D(source.u) } } object Usage { import Implicits._ def conv[F,T](source : F)(implicit ev : Convert[F,T]) : T = ev.convert(source) val a = A(0) val b = conv[A,B](a) val c = conv[A,C](a) val d = conv[A,D](a) } 这种方法使A – > B – > C – > D和A – > B – > D,编译器选择后一种路由.但是如果有任何分支,它会失败 持续传球 abstract class PostConvert[F,T](mt : Convert[M,T] { def pre(source : F) : M override def convert(source : F) : T = mt.convert( pre(source) ) } class IdConvert[F]() extends Convert[F,F] { override def convert(source : F) : F = source } object ImplicitsPost { implicit def idConvert[F] : Convert[F,F] = new IdConvert[F]() implicit def a2b[T](implicit mt : Convert[B,T]) = new PostConvert[A,B,T](mt) { override def pre(source : A) : B = B(source.u) } implicit def a2bb[T](implicit mt : Convert[BB,BB,T](mt) { override def pre(source : A) : BB = BB(source.u) } implicit def b2c[T](implicit mt : Convert[C,T]) = new PostConvert[B,C,T](mt) { override def pre(source : B) : C = C(source.u) } implicit def c2d[T](implicit mt : Convert[D,T]) = new PostConvert[C,D,T](mt) { override def pre(source : C) : D = D(source.u) } /*implicit def b2d[T](implicit mt : Convert[D,T](mt) { override def pre(source : B) : D = D(source.u) }*/ // compiler fails } object UsagePost { import ImplicitsPost._ def conv[F,D](a) } 在这种情况下,编译器可以忽略不相关的A – > BB转换.但是它无法解决冲突A – > B – > C – > D和A – > B – > e 我在寻找什么 以一般方式解决问题的一些方法.我可以定义关系图,让隐式力学选择最短的方法.如果我可以调整每个转换重量来使A – > B – > D和A – > C – > D可区分隐藏的分辨率优先级有一些黑魔法,我希望它可以帮助. 据说Implicits是非常计算功能强大的仪器,几分钟的编译工作值得在复杂的情况下.所以我希望通过一些棘手的技术可以进行任意长时间的转换. 解决方法
简短答案
您无法使用scala隐式解决方案解决此问题,因为它不支持回溯. 实际答案 最好的办法是修改scala编译器以支持隐式解析的回溯,这应该足以让您的第一个实现工作. 长答案 我说谎,应该是可能的编译器的当前状态,但它不会像你写的等效的Prolog程序一样好,可能是超出了“哦,这应该是有趣的写在类型级别“).让我先说一下你的问题. 给出几种类型: trait A; trait B; trait B; trait C; trait D 和这些类型的有向图: trait Edge[X,Y] def fact[X,Y] = new Edge[X,Y] {} implicit val edge0: Edge[A,B] = fact // ( A ) implicit val edge1: Edge[A,BB] = fact // ↓ ↓ implicit val edge2: Edge[B,C] = fact // ( B ) BB implicit val edge3: Edge[B,D] = fact // ↓ ↓ implicit val edge4: Edge[C,D] = fact // C → D 使用隐式分辨率找到A和D之间的最短路径. 要看看这个问题是棘手的,把它分解成两部分是有用的: >将这些隐式边缘提取为从节点A开始的图形类型级别表示形式,如下所示: A :: (B :: (C :: (D :: HNil) :: HNil) :: (D :: HNil) :: HNil) :: (BB :: HNil) :: HNil >对此表示执行类型级BFS. 令人惊讶的是,1.听起来很棘手,那是因为scala隐含的分辨率不会回溯.您将不得不采用稍微不同的图形表示形式来实现. 一个解决方案,坚持你的原始配方(每个边缘隐含的),可能是使用类似于this example中暴露的技术,它使用两个特征EdgeLeft [X,Y]&特征EdgeRight [X,Y],而不是特征边缘[X,并将图形的所有边缘收集到单个HList中,有效地解决了缺乏回溯. 您也可以通过对更接近邻接矩阵的表示进行编码,例如使用隐式事实(即邻居[A,B :: BB :: HNil])使您的生活变得更简单.但是无论哪种方式,您的图表表示方式稍作改动都应足以让您构建与上述图形表示相当的结构. 解决2.不容易,但从理论上说,在以下输入中写出纯粹的价值级DFS,并将其提升到类型级别理论上应该不难: val input: List[Any] = ( "A" :: ("B" :: ("C" :: ("D" :: Nil) :: Nil) :: ("D" :: Nil) :: Nil) :: ("BB" :: Nil) :: Nil ) def DFS(i: List[Any]): List[String] = ??? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |