如何在Scala中以递归方式应用隐式转换

如何在Scala中以递归方式应用隐式转换,scala,recursion,implicits,Scala,Recursion,Implicits,我正在尝试编写一个转换库,用于将一些scala类型转换为HTML表示。比如说,我想执行列表(1,2).toHtml并获得12,作为字符串 到目前为止,我已经编写了一组隐式转换,可以很好地检测结果类型并将正确的应用到html 让我举一个例子: object Conversions { implicit def fromIterable[A](l : Iterable[A]) = new Object { def toHtml = <ul> { l.map{ e =&

我正在尝试编写一个转换库,用于将一些scala类型转换为HTML表示。比如说,我想执行
列表(1,2).toHtml
并获得
  • 1
  • 2
    • ,作为
      字符串

      到目前为止,我已经编写了一组隐式转换,可以很好地检测结果类型并将正确的
      应用到html

      让我举一个例子:

      object Conversions {
      
        implicit def fromIterable[A](l : Iterable[A]) = new Object {
             def toHtml = <ul> { l.map{ e => <li>{ e }</li> } } </ul> toString
        }
      }
      import Conversions._
      
      对象转换{
      隐式def fromIterable[A](l:Iterable[A])=新对象{
      def toHtml=
        {l.map{e=>
      • {e}
      • }
      toString } } 导入转换_
      有了这段代码,每当我询问编译器
      List(1,2).toHtml
      时,我都会得到正确的转换。与任何其他
      Iterable
      val一样

      我的问题是如何递归地使用这个toHtml转换?因为如果我键入
      List(List(1,2),List(3,4)).toHtml
      我想得到
              • toHtml
                转换递归地应用到输入的每个元素

                我试图将
                toHtml
                定义更改为
                deftohtml=
                  {l.map{e=>{li>{e.toHtml}}
                toString
                ,但该定义不起作用,因为编译器告诉我
                toHtml的值不是类型参数a的成员,这非常合理。
                我知道我的问题可能存在于
                新对象{…}
                中,我从
                fromIterable[A]
                隐式定义返回,该隐式定义可能返回一个具有特征或其他内容的类。
                我已经阅读了很多关于隐式的内容,但是我还没有弄明白,在不去参数化fromIterable签名和定义几个特定的例子的情况下,如何将这个
                递归地应用到HTML
                转换,比如
                fromIterable(l:List[List[Any]])
                或者类似的东西

                你们能不能请我给我一些建议,如何实现它,我做错了什么


                谢谢

                首先让我们看看如何在没有隐式/对象和转换的情况下完成这项工作。我还删除了
                toString
                ,但可以随时添加它

                def toHtml[A](l:Iterable[A]):scala.xml.Elem  = 
                  <ul> {l.map( _ match{ 
                    case e:Iterable[_] => <li>{toHtml(e)}</li> 
                    case e => <li>{e}</li>
                  })}</ul>
                
                // Exiting paste mode, now interpreting.
                
                toHtml: [A](l: Iterable[A])scala.xml.Elem
                scala> toHtml(List(List(1,2), List(3,4)))
                res17: scala.xml.Elem = <ul> <li><ul> <li>1</li><li>2</li></ul></li><li><ul> <li>3</li><li>4</li></ul></li></ul> 
                
                def-toHtml[A](l:Iterable[A]):scala.xml.Elem=
                
                  {l.map({match{ 案例e:Iterable[\u]=>
                • {toHtml(e)}
                • 案例e=>
                • {e}
                • })}
                //正在退出粘贴模式,现在正在解释。 toHtml:[A](l:Iterable[A])scala.xml.Elem scala>toHtml(列表(列表(1,2),列表(3,4))) res17:scala.xml.Elem=
                • 1
                • 2
                  • 3
                  • 4
                现在让我们重新包装所有内容,以回答您的问题:

                object Conversions {
                  implicit def fromIterable[A](l : Iterable[A]):Object{def toHtml:xml.Elem} = new Object {
                    def toHtml:xml.Elem  = 
                      <ul>{l.map( _ match{ 
                        case e:Iterable[_] => <li>{e.toHtml}</li> 
                        case e => <li>{e}</li>
                      })}</ul>
                  }
                }
                import Conversions._
                
                对象转换{
                隐式def fromIterable[A](l:Iterable[A]):对象{def toHtml:xml.Elem}=new对象{
                def toHtml:xml.Elem=
                
                  {l.map({match{ 案例e:Iterable[\u]=>
                • {e.toHtml}
                • 案例e=>
                • {e}
                • })}
                } } 导入转换_
                这将产生所需的结果:

                scala> List(List(1,2), List(3,4)).toHtml
                res3: scala.xml.Elem = <ul><li><ul><li>1</li><li>2</li></ul></li><li><ul><li>3</li><li>4</li></ul></li></ul>
                
                scala>List(List(1,2),List(3,4)).toHtml
                res3:scala.xml.Elem=
                • 1
                • 2
                  • 3
                  • 4
                几句话:

                • 其次,我必须给出“fromIterable”方法的返回类型。我确信存在更好的解决方案,但我对隐式转换没有太多经验
                • 最后,如果有人提出建议,我很乐意编辑我的答案。(或随意编辑)

                您可以通过以下方式优雅、安全地解决此类问题:

                然后:

                scala> printer format messy.toHtml
                res1: String = 
                <ul>
                  <li>
                    <ul>
                      <li>
                        <ul>
                          <li>a</li>
                        </ul>
                      </li>
                    </ul>
                  </li>
                  <li>
                    <ul>
                      <li>
                        <ul>
                          <li>b</li>
                          <li>c</li>
                        </ul>
                      </li>
                      <li>
                        <ul>
                          <li>c</li>
                        </ul>
                      </li>
                    </ul>
                  </li>
                </ul>
                
                scala>打印机格式messy.toHtml
                res1:字符串=
                
                    • a
                    • b
                    • c
                    • c

                有关上述方法的更详细说明,请参见我的答案。

                非常感谢您,我想这正是我想要的!我仍然不能完全确定,但我会在我的工作中集成这一点,我会让你知道的。至于你得到的警告,你只需从签名中删除B,然后写
                案例e:Iterable[\u]=>…
                ,它就可以工作了。非常感谢!你的解决方案看起来更优雅,更接近我认为我必须做的事情。它使用了一个trait来定义某个类型将有一个toHtml方法,以便我可以应用它。然而,当我尝试您的代码时,我不知道如何将其应用于Iterables,而不仅仅是列表。。。有什么提示吗?将
                Markup
                的类型参数设置为反变量,并在
                Markup
                方法中将
                List
                更改为
                Iterable
                ,就可以全部设置了。我会在有机会的时候更新答案。感谢迈尔斯·萨宾的帮助,这可以简化一点。感谢你的努力:)你可以编写类似于
                隐式def-mapMarkup[a:Markup,B:Markup]:Markup[Map[a,B]]
                ,但是你需要一个特征和对象来区分隐式的优先级(这样地图就不会被当作一个可数了)。今晚我会尝试更新答案。
                val messy = List(List(List("a")), List(List("b", "c"), List("c")))
                
                val printer = new xml.PrettyPrinter(80, 2)
                
                scala> printer format messy.toHtml
                res1: String = 
                <ul>
                  <li>
                    <ul>
                      <li>
                        <ul>
                          <li>a</li>
                        </ul>
                      </li>
                    </ul>
                  </li>
                  <li>
                    <ul>
                      <li>
                        <ul>
                          <li>b</li>
                          <li>c</li>
                        </ul>
                      </li>
                      <li>
                        <ul>
                          <li>c</li>
                        </ul>
                      </li>
                    </ul>
                  </li>
                </ul>