强制类型参数为特征

时间:2017-02-07 23:32:44

标签: scala types

我有以下代码;

object main
{
    def main(args: Array[String]): Unit = 
    {
        trait E
        {
            def test(): Unit = println("test :)")
        }

        class B[T](val x: Int) 
        {
            def inc(): B[T] with T = new B[T](x + 1) with T
        }

        class A[T](f : B[T] with T => Unit)
        {
            def apply(b: B[T] with T) = f(b)
        }

        val b = new B[E](0) with E
        val a = new A[E](b => b.test())(b)
    }
}

然而,行def inc(): B[T] with T = new B[T](x + 1) with T没有编译,给出错误"类类型必需但T找到"并且" T需要成为混合的特征。我理解为什么会这样,但我无法找到解决问题的方法!我还没有办法约束T成为一种特质,这让我担心这种方法不会起作用......

为了更好地说明为什么我要尝试实现这一点(只是任何人都可以提供更好的解决方案)我有一个Parsec[S, U, E, A]类,它由接受{{State[S, U, E] with E的函数组成。 1}}对象。我们的想法是U是用户指定的状态,A是解析器的结果,S是令牌流,E是状态的某种扩展(对于例如,人们可能希望创建Parsec[Stream[String, Char], Int, IndentationSensitive, List[Expr]]等等。U = Int将是用户想要计算的内容,并且不应该干扰缩进所需的状态灵敏度(两个Ints)将通过混合IndentationSensitive特征提供。然后,如果用户想要一些其他功能,他们可以继续混合解析器的更多特征。

那么,无论如何我可以约束代码中的类型参数T,这样我就可以将它混合成B,或者如果没有,是否有更好的方法来完成我需要的东西?

如果我真的不清楚我想要完成什么,那么关于CodeReview的this问题说明了这种情况(在 很多 更多细节)。但Parsec[S <: Stream[_, _], U, A]Parsec[S <: Stream[_, _], U, E, A]取代,而州和所有其他部分则相同。

2 个答案:

答案 0 :(得分:0)

  

为了更好地说明为什么我要尝试实现这一点(只是任何人都可以提供更好的解决方案)我有一个Parsec[S, U, E, A]类,它由接受{{State[S, U, E] with E的函数组成。 1}}对象。这个想法是U是用户给定的状态,A是解析器的结果,S是令牌流,E是状态的某种扩展(例如,人们可能希望创建Parsec[Stream[String, Char], Int, IndentationSensitive, List[Expr]]

在这种情况下,我只需向val extension: E添加State字段,然后将功能更改为接受State[S, U, E]。如果您真的想要,可以添加从State[S, U, E]E的隐式转换,这样功能就可以直接访问E个成员,但我可能不会自己做。

答案 1 :(得分:0)

我设法找到问题的解决方案,这并不理想,但它确实解决了系统的其他几个问题。也就是说,当我们创建new State[S, U, E](input, pos, state) with E时,使用E添加的变量会发生什么。他们迷路了,这是一个杀手。

让我们定义一个新类型type StateBuilder[S <: Stream[_, _], U, E] = (Option[State[S, U, E] with E], S, SourcePos, U) => State[S, U, E] with E。这个函数可以构造一个我们想要的类型的新状态,给定一个可能的先前状态和一些新状态的状态&#34;正常&#34;参数。

现在我们可以将州重新定义为;

case class State[S <: Stream[_, _], U, E](stateInput: S, statePos: SourcePos, stateUser: U, build: StateBuilder[S, U, E])

现在我们只需要一些StateBuilder[S, U, E],它们将在状态之间传递,但我们需要在创建初始状态时将其输入。这没关系,但这意味着用户需要了解它们是什么(这有点不利)。没有扩展名的示例构建器;

trait Default
object Default
{
    def build[S <: Stream[_, _], U](s: Option[State[S, U, Default] with Default], ts: S, pos: SourcePos, u: U): State[S, U, Default] with Default =
    {
        new State[S, U, Default](ts, pos, u, build) with Default
    }
}

可能会更复杂;

trait IndentationSensitive
{
    var stateLevel: Int = 0
    var stateRequiredIndent: Int = 0
}
object IndentationSensitive
{    
    def build[S <: Stream[_, _], U](s: Option[State[S, U, IndentationSensitive] with IndentationSensitive], ts: S, pos: SourcePos, u: U): State[S, U, IndentationSensitive] with IndentationSensitive =
    {
        val s_ = new State[S, U, IndentationSensitive](ts, pos, u, build) with IndentationSensitive
        s match
        {
            case Some(s) => 
                s_.stateLevel = s.stateLevel
                s_.stateRequiredIndent = s.stateRequiredIndent
            case None => 
                s_.stateLevel = 0
                s_.stateRequiredIndent = 0
        }
        s_
    }
}

为了组合扩展,用户需要手工构建构建器功能,但是任何人都无法理解如何做到这一点并不合理。能够为每种类型自动构建这些都是很好的,但这是一个不同的问题。