È possibile avere un polimorfismo Ad-Hoc con l'invio in runtime?

2

Come ho capito e come descritto qui , il polimorfismo ad-hoc è limitato alla spedizione in fase di compilazione. Cioè, se abbiamo una funzione che si aspetta un argomento che appartiene a un typeclass, dobbiamo conoscere il tipo concreto dell'argomento in fase di compilazione per poter usare questa funzione.

Quindi il seguente (in pseudocodice simile a Haskell) non sarà valido:

// C-style comments

typeclass Num(X) where:
    add :: X -> X -> X
    one :: X

implementation Num for Int:
    add x y = x + y
    one = 1

implementation Num for Rational:
    // analogous thing…

veryCleverFunction :: Num a -> a -> a
veryCleverFunction x = add x one

tooCleverFunction :: Int -> Num(?)
tooCleverFunction x = {
    y =
        if (x > 0)
            x
        else
            Rational(x, 2) // 'y' has a value of unknown type,
                           // but we know it's under 'Num' typeclass
    veryCleverFunction y // can work with any 'Num's, but here the concrete type is unknown…
}

(Spero sia chiaro cosa voglio rappresentare in questo frammento)

Almeno in Scala sono quasi sicuro che non ci sia modo di esprimerlo. O mi sbaglio? Ed è possibile in teoria? Ci sono delle lingue che consentono questo?

    
posta Sarge Borsch 10.10.2014 - 19:26
fonte

2 risposte

3

Esistono diversi meccanismi di polimorfismo. Ad eccezione del polimorfismo parametrico, questi flussi di controllo di distribuzione dipendono dal tipo di argomenti della funzione. In altre parole: sono una specie di overloading di funzioni . Gli argomenti possono essere considerati per il loro tipo statico o per il loro tipo dinamico. Se inviamo una chiamata su un tipo statico, chiamiamo questo polimorfismo ad-hoc . Se inviamo il tipo dinamico, è single dispatch se consideriamo solo il tipo dinamico di un argomento (in genere un oggetto su cui viene chiamato un metodo) o dispatch multiplo se consideriamo il tipo dinamico di tutti gli argomenti.

Tutte le lingue OOP devono almeno offrire una singola spedizione. Il pattern Visitor può essere utilizzato per simulare dispatch multipli utilizzando livelli aggiuntivi di riferimento indiretto, ma non è pratico per più di due argomenti. Multi-dispatch in generale risolve lo stesso tipo di problemi che il polimorfismo ad-hoc fa, tranne che accade a runtime e può quindi essere più espressivo. Esempi degni di nota di lingue con multi-dispatch (a volte chiamati multi-metodi) sono il Common Lisp Object System (CLOS) e la lingua di Julia.

Il post del blog a cui ti sei collegato dimostra un polimorfismo ad-hoc, ma in modo eccessivamente complicato. L'esempio più semplice potrebbe essere:

abstract class Pet
class Cat extends Pet
class Dog extends Pet

object Owner {
    def pet(cat: Cat) = "You can haz cuddles!"
    def pet(dog: Dog) = "Who's a good boy?"
}

val cat: Cat = new Cat()
val dog: Dog = new Dog()
val pet: Pet = new Cat()

Owner pet cat // works
Owner pet dog // works
Owner pet pet // fails, because implementation is chosen *statically*

È la stessa storia in Java e C ++. Il post del blog diventa quindi tangente su come utilizzare i parametri impliciti per implementare i metodi di estensione e come possono essere utilizzati per nascondere il pattern del visitatore. Niente di tutto questo è davvero rilevante per il polimorfismo ad-hoc.

Il tuo esempio di codice ha dei problemi in quanto i tipi di ritorno di tooCleverFunction ma, più importante, del condizionale assegnato alla variabile y non sono ben definiti. Poiché y può essere Int o Rational , il tipo di controllo deve rifiutare il tuo codice. Per consentire in modo sicuro questa ambiguità, potremmo introdurre un tipo Union . Ma se abbiamo y :: Union Int Rational , allora veryCleverFunction non può essere applicato a y (eccetto ovviamente se Union è un functor in modo che possiamo fare fmap veryCleverFunction y , che restituisce un altro Union per te da trattare con). Se usiamo una tale unione, nessuna informazione di tipo statico viene persa. Invece, devi affrontare il casino che hai creato nel resto del programma.

Vorresti y per avere invece il tipo Num a . Ma Num a non è un tipo, ma una classe di tipo, cioè una restrizione sul tipo a .

    
risposta data 11.10.2014 - 13:56
fonte
0

Il tuo esempio mostra già perché non è possibile. Considera one . Se hai incontrato one in fase di esecuzione, dovrebbe valutare Int o Rational ? Non c'è modo di decidere.

Una risorsa di lettura correlata è classi OOP vs type su Haskell Wiki.

    
risposta data 10.10.2014 - 21:55
fonte

Leggi altre domande sui tag