简体   繁体   English

强制类型参数为特征

[英]Force type parameter to be a trait

I have the following code; 我有以下代码;

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)
    }
}

However, the line def inc(): B[T] with T = new B[T](x + 1) with T does not compile, giving the error that "class type required but T found" and "T needs to be a trait to mix in". 但是, def inc(): B[T] with T = new B[T](x + 1) with T的行def inc(): B[T] with T = new B[T](x + 1) with T不能编译,给出“需要类类型但是找到T类”和“T需要”的错误混合的特质“。 I understand why this is the case, but I can't find a way to work around it! 我明白为什么会这样,但我找不到办法解决它! I haven't found a way to constraint T to be a trait, which makes me fear that this approach won't work... 我还没有找到一种方法来限制T成为一种特质,这让我担心这种方法不起作用......

To give some more background on why I'm trying to achieve this (just incase anyone can offer a better solution) I have a Parsec[S, U, E, A] class, which is built up of functions that accept a State[S, U, E] with E object. 为了给出更多关于我为什么要实现这个目标的背景(只是任何人都可以提供更好的解决方案)我有一个Parsec[S, U, E, A]类,它由接受State[S, U, E] with E的函数构成State[S, U, E] with E对象。 The idea is that U is a user given state, A is the result of the parser, S a stream of tokens and E is some extension of the state (for instance, one might wish to create a Parsec[Stream[String, Char], Int, IndentationSensitive, List[Expr]] etc etc. The U = Int would be something the user wanted to count for instance, and that shouldn't need to interfere with the state required for the Indentation sensitivity (which is two Ints) which would be provided by mixing in the IndentationSensitive trait. Then if the user wanted some other functionality they can keep mixing in more traits for the parsers. 这个想法是U是用户给定的状态, A是解析器的结果, S是令牌流, E是状态的某种扩展(例如,人们可能希望创建Parsec[Stream[String, Char], Int, IndentationSensitive, List[Expr]] U = Int将是用户想要计数的东西,并且不需要干扰压痕灵敏度所需的状态(这是两个Ints) Parsec[Stream[String, Char], Int, IndentationSensitive, List[Expr]]等等。这将通过混合IndentationSensitive特性提供。然后,如果用户想要一些其他功能,他们可以继续混合解析器的更多特征。

So, is there anyway I can constraint the type parameter T in the code so that I can mix it into a B , or if not, is there a better way of accomplishing what I need? 那么,无论如何我可以约束代码中的类型参数T ,这样我就可以将它混合成B ,或者如果没有,是否有更好的方法来完成我需要的东西?

If it really isn't clear what I'm trying to accomplish then this question on CodeReview illustrates the situation (in a lot more detail). 如果真的不清楚什么,我试图完成,然后对代码审查问题的说明(在很多更详细)的情况。 But Parsec[S <: Stream[_, _], U, A] is replaced by Parsec[S <: Stream[_, _], U, E, A] and the same for State and all the other parts. 但是Parsec[S <: Stream[_, _], U, A]Parsec[S <: Stream[_, _], U, E, A]取代,而State和所有其他部分则相同。

To give some more background on why I'm trying to achieve this (just incase anyone can offer a better solution) I have a Parsec[S, U, E, A] class, which is built up of functions that accept a State[S, U, E] with E object. 为了给出更多关于我为什么要实现这个目标的背景(只是任何人都可以提供更好的解决方案)我有一个Parsec[S, U, E, A]类,它由接受State[S, U, E] with E的函数构成State[S, U, E] with E对象。 The idea is that U is a user given state, A is the result of the parser, S a stream of tokens and E is some extension of the state (for instance, one might wish to create a Parsec[Stream[String, Char], Int, IndentationSensitive, List[Expr]] 这个想法是U是用户给定的状态,A是解析器的结果,S是令牌流,E是状态的某种扩展(例如,人们可能希望创建Parsec[Stream[String, Char], Int, IndentationSensitive, List[Expr]]

In this case I'd just add a val extension: E field to State and change the functions to accept State[S, U, E] . 在这种情况下,我只需添加一个val extension: E领域的State和转变职能,接受State[S, U, E] If you really want, you can add an implicit conversion from State[S, U, E] to E , so the functions can access E 's members directly, but I probably wouldn't do it myself. 如果你真的想要,你可以添加从State[S, U, E]E的隐式转换,这样函数可以直接访问E的成员,但我可能不会自己做。

I've managed to find a solution to the problem, It's not ideal, but it does deal with a couple of other problems with the System. 我已经设法找到问题的解决方案,它并不理想,但它确实解决了系统的其他几个问题。 Namely, when we create a new State[S, U, E](input, pos, state) with E what is meant to happen with the variables added with E . 也就是说,当我们创建了一个new State[S, U, E](input, pos, state) with E指的是什么与添加的变量发生E They get lost and that's a killer. 他们迷路了,这是一个杀手。

Let's define a new type type StateBuilder[S <: Stream[_, _], U, E] = (Option[State[S, U, E] with E], S, SourcePos, U) => State[S, U, E] with E . 让我们定义一个新的类型type StateBuilder[S <: Stream[_, _], U, E] = (Option[State[S, U, E] with E], S, SourcePos, U) => State[S, U, E] with E This is a function that can construct a new State of the type we want, given a possible previous state and some new values for the state's "normal" parameters. 这个函数可以构造一个我们想要的新状态,给定一个可能的先前状态和一些状态的“正常”参数的新值。

Now we can redefine the State as; 现在我们可以将国家重新定义为;

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

And now we just need some of those StateBuilder[S, U, E] , which will get passed between states, but we need to feed it in when we create the initial state. 现在我们只需要一些StateBuilder[S, U, E] ,它们将在状态之间传递,但我们需要在创建初始状态时将其输入。 That's fine, but it means the user needs to understand what they are (which is a little disadvantageous). 这很好,但这意味着用户需要了解它们是什么(这有点不利)。 An example builder for no extensions; 没有扩展的示例构建器;

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
    }
}

and a more complicated one might be; 一个更复杂的可能是;

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_
    }
}

In order to compose extensions, the user would need to hand construct the builder function, but that's not unreasonable for anyone to be able to work out how to do. 为了组成扩展,用户需要手工构建构建器功能,但任何人都无法理解如何操作并不是不合理的。 It would be nice to be able to automatically build these for each type, but that's a different question. 能够为每种类型自动构建这些都会很好,但这是一个不同的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM