2016-12-07 3 views
1

Ich suche nach einer eleganten Möglichkeit, Teilfunktionen zu verketten, die von einem gemeinsamen Basistyp abgeleitet sind. Die Idee ist, dass jeder Teilfunktion eine Art behandelt, so dass sie leicht werden für verschiedene Arten zu komponieren und haben eine gemeinsame catch-all, wenn die verkettete Teilfunktion nicht definiert ist:Elegante Möglichkeit, Scala Teilfunktionen zu verketten

trait Message 
trait SysMessage extends Message 
trait UserMessage extends Message 

case class TextSysMessage(m: String) extends SysMessage 
case class TextUserMessage(m: String) extends UserMessage 

class Test { 

    type MessagePF = scala.PartialFunction[Message, String] 
    type SysMessagePF = scala.PartialFunction[SysMessage, String] 
    type UserMessagePF = scala.PartialFunction[UserMessage, String] 

    def getSysMessage: SysMessagePF = { 
    case sm: TextSysMessage ⇒ s"System message: ${sm.m}" 
    } 

    def getUserMessage: UserMessagePF = { 
    case um: TextUserMessage ⇒ s"User message: ${um.m}" 
    } 

    def * : MessagePF = { 
    case m ⇒ s"Unknown message: $m" 
    } 

    // Chained partials fails because `m` is a SysMessage with UserMessage 
    def handler(m: Message): String = (getSysMessage orElse getUserMessage orElse *)(m) 
} 

Offensichtlich ist dieser Ansatz nicht kompiliert. Ich kann durch verschachtelte Muster wie diese

def getSysMessage: MessagePF = { 
    case m: SysMessage ⇒ m match { 
    case sm: TextSysMessage ⇒ s"System message: ${sm.m}" 
    } 
} 

passend dieses Problem umgehen, aber dann verliere ich die Fähigkeit, alle in einem Fang unbekannte Umgang mit Nachrichten. Gibt es einen eleganten Weg, dieses Ziel zu erreichen?

type MessagePF = scala.PartialFunction[Message, String] 
type SysMessagePF = scala.PartialFunction[Message, String] 
type UserMessagePF = scala.PartialFunction[Message, String] 
+4

Warum nicht jede 'PartialFunction' mit' Message' als Eingabetyp definieren? – adamwy

+0

Das ist im Wesentlichen Plan B .... –

+0

Aber wie können Sie sogar eine partielle Funktion erwarten, die konkreten Subtyp mit arbiträren 'Message' arbeiten lässt? Etwas wie 'def handler (m: Nachricht): String = (getSysMessage) (m)' wird auch nicht funktionieren. – adamwy

Antwort

2

Als Ergänzung + Hongxu Chens Antwort auf @adamwy, können Sie Ihren ganz eigenen combinator definieren, die impliziten Parameter beinhaltet,:

3

Wie @adamwy vorgeschlagen, können Sie die Teilfunktion Typen sein ändern so erzwingt leicht unterschiedliche Anwendungssyntax

implicit class PartFuncOps[A: ClassTag, B](pf: PartialFunction[A, B]) { 
    def or[D >: A, C <: D : ClassTag](other: PartialFunction[C, B]): PartialFunction[D, B] = { 
    case a: A if pf.isDefinedAt(a) ⇒ pf(a) 
    case c: C if other.isDefinedAt(c) ⇒ other(c) 
    } 
} 

Jetzt können Sie schreiben

def combine = getSysMessage or getUserMessage or * 
def handler(m: Message): String = combine(m) 

Oder

def handler(m: Message): String = (getSysMessage or getUserMessage or *).apply(m) 
+1

Oder verwenden Sie nur einen Typ für alle. – adamwy

Verwandte Themen