Вопрос

This is starting to be a pretty common situation for me:

  trait CoVariant[+T] {
    def bar: T
  }

  def curried[T](x: String)(y: CoVariant[T]) = y // Not really, but for simplicity
  val applied = curried("foo") _

  val bar = new CoVariant[String] {
    def bar = "bar"
  }
  applied(bar)

So I have something that's co-variant and I need to do some common stuff first and then have a generic part left unapplied. I would really like to be able to write it just like a curried function like above, but that of course yields a compile error on the last line:

- type mismatch;  found   : CoVariant[String]  required: CoVariant[Nothing]

I seem to resort to just introducing a class just to have someplace to stick the type parameter so it can be left abstract:

trait StupidWrapper {
   def apply[T](y: CoVariant[T]) : CoVariant[T]
}

and instead of the curried thing have

def notAsNice(x: String) = new StupidWrapper {
   def apply[T](y: CoVariant[T]) = y
}

so now:

val applied = notAsNice("foo")
applied(bar)  

compiles.

This feels stupid and I guess there is better ways?

Update:

I think I'll better concretize. What I have is this:

  abstract class ParserToSeq {
    def apply[T](parser: Parser[T]): Seq[T]
  }

  def fromTrainingData(trainingLines: Seq[String]) = new ParserToSeq {
    def apply[T](p: Parser[T]) = trainingLines.map(parseAll(p, _)).map {
      _ match {
        case Success(wt, _) => Some(wt)
        case _ => None
      }
    }.flatten
  }

and then

val thisData = fromTrainingData(trainingLines)
lazy val wordTags = thisData(wordtagParser) // Parser[WordTag]
lazy val uniGrams = thisData(uniGramParser) // Parser[UniGram]
…

I just want to do away with the ParserToSeq and do:

def fromTrainingData[T](trainingLines: Seq[String])(p: Parser[T]) = 
  trainingLines.map(parseAll(p, _)).map {
  _ match {
    case Success(wt, _) => Some(wt)
    case _ => None
  }
}.flatten

Note that the T here is specific for each parser. The problem as I understand it that the T is resolved to a concrete type on the partially applied method even though the type actually applies to the second parameter list.

It seems I would like to kindof stick the declaration of the type param somewhere in the middle:

def fromTrainingData(trainingLines: Seq[String])[T](p: Parser[T])

which of course isn't valid Scala.

Это было полезно?

Решение

As you mention, scala Function objects don't carry type parameters.

However, you can replace ParseToSeq with a more generic ~> that takes higher-kinded types,

trait ~>[A[_],B[_]] {
  def apply[X](a : A[X]) : B[X]
}

Then your example becomes,

trait Parser[T]
trait WordTag
trait UniGram
val trainingLines: Seq[String] = ???
val wordtagParser: Parser[WordTag] = ???
val uniGramParser: Parser[UniGram] = ???

def fromTrainingData[T](trainingLines: Seq[String]) = new ~>[Parser,Seq] {
  def apply[T](p: Parser[T]): Seq[T] = ???
}
val thisData = fromTrainingData(trainingLines)
lazy val wordTags = thisData(wordtagParser) // Seq[WordTag]
lazy val uniGrams = thisData(uniGramParser) // Seq[UniGram]
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top