2017-05-26 8 views
3

Ich möchte einen Typ wie LimitedString[Limit] konstruieren, wobei Limit eine Typdarstellung der maximalen Länge der Zeichenkette ist.Wie man vorab einen formlosen Singletontyp angibt

Es wäre

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) = { ... } 

entlang der Linien der Arbeit Was kann ich nicht herausfinden, wie eigentlich (wie in der Tastatur) eingeben, um den Typ (wie in Kompilierung) für Limit.

Ich begann Shapeless des Singleton-Typen suchen in und festgestellt, dass Sie

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

sagen kann, aber wenn ich versuche, Int(100) als Typ zu verwenden, bekomme ich Fehler.

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

Zusätzlich, wie würde ich etwas wie def getLimit[Limit]: Int implementieren?

+0

zu interagieren Warum nicht Church-Codierung verwenden, durch unförmige 'Nat'. Zahlen als Typen darzustellen, scheint der genaue Zweck zu sein (für Zahlen <400). Wenn Sie größer brauchen, überprüfen Sie [hier] (https://stackoverflow.com/questions/21296099/limits-of-nat-type-in-shapeless) für Ideen/Diskussion, was mit größeren Zahlen zu tun ist. –

+1

Ich kann im Moment keine vollständige Antwort geben, aber werfen Sie einen Blick auf Shapeless's 'Witness', mit dem Sie sich auf solche Typen wie z. 'Witness. \' 100 \ '.T' und bietet Zugriff auf ihre Laufzeitwerte. –

+0

@DavisBroda Das beabsichtigte Limit liegt in der Nähe von 100. Würde das bedeuten, dass ich Succ [Succ [Succ .....]]] auf 100 Ebenen schreiben müsste, um meinen "Str100" -Typ-Alias ​​zu erstellen ? – Dylan

Antwort

2

Ich habe @ TravisBrown Vorschlag in Shapless des Witness, zu suchen und kam mit dieser:

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

Verbrauch:

import shapeless._ 

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

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

Die wichtigsten Dinge, die es funktioniert:

  • Witness.Aux ist eine Typklasse, die Sie verwenden können, um den Singletonzu erhaltenwieder aus dem Typ
  • Der Singleton-Typ Witness.`50`.T ist eigentlich ein Subtyp von Int
  • Typ Aliase macht es bequemer mit
+0

Ein Nachteil des "Companion" ist, dass der Compiler nicht automatisch nach Implikationen sucht, die 'Name' seit' Name beinhalten 'ist nur ein Typalias. – Dylan

Verwandte Themen