How to specify a shapeless singleton type ahead of time

I would like to construct a type of type LimitedString[Limit]

, where Limit

is the type representation of the maximum string length.

It would work line by line

class LimitedString[Limit] [private](val s: String)
object LimitedString {
  private def getLimit[Limit]: Int = ??? // turn `Limit` type into a value
  def truncate[Limit](s: String) = new LimitedString[Limit](s take getLimit)
  def get[Limit](s: String) = 
    if(s.length < getLimit) Some(new LimitedString[Limit](s))
    else None
}

type Str100 = LimitedString[100] // obviously this won't work
def someLibraryMethod(s: Str100) = { ... }

      

I can't figure out how to actually type (as in the keyboard) the type (as in compilation) for Limit

.

I started looking into the unsophisticated singleton types and found that you can say

100.narrow
// res1: Int(100) = 100

      

But if I try to use Int(100)

as a type, I get errors.

val x: Int(100) = 100
// error: ';' expected but '(' found.

      

Also, how would I implement something like def getLimit[Limit]: Int

?

+3


source to share


1 answer


I took @TravisBrown's suggestion to take a look at Shapless Witness

and came up with this:

class LimitedString[Limit <: Int] private[util](val s: String) extends AnyVal {
    override def toString = s
}
class LimitedStringCompanion[Limit <: Int : Witness.Aux]{
    def limit: Int = implicitly[Witness.Aux[Limit]].value

    def unapply(s: String): Option[LimitedString[Limit]] = {
        if(s.length > limit) None else Some(new LimitedString(s))
    }

    def truncate(s: String): LimitedString[Limit] = new LimitedString(s take limit)
}

      

Using:



import shapeless._

object MyLibraryThing {
  type Name = LimitedString[Witness.`50`.T]
  object Name extends LimitedStringCompanion[Witness.`50`.T]

  def rename(id: Int, name: Name) = { ... }
}

      

The main things that make it work:

  • Witness.Aux

    is the typeclass you can use to infer the singleton value

    back from the type
  • A single type Witness.`50`.T

    is actually a subtypeInt

  • Typical aliases make it easier to interact with
+2


source







All Articles