Classe come oggetto di prima classe

7

Una classe può essere un oggetto di prima classe? Se sì, come sarebbe la sintassi per la creazione dinamica di nuove classi?

Per restringere la domanda: come daresti questa funzionalità mantenendo il linguaggio coerente?

Ad esempio come si crea riferimento per il nuovo tipo. Anche tu fai riferimento a oggetti di prima classe e poi usi qualcosa di simile:

Reference<newType> r = new Reference<newType>();
r.set(value);

Beh, questo potrebbe diventare complicato, quindi potresti costringere l'utente a usare i riferimenti al tipo di oggetto per le classi create dinamicamente, ma poi perdi il controllo dei tipi.

Penso che la creazione di sintassi concisa per questo sia un problema interessante che la risoluzione potrebbe portare a una migliore progettazione linguistica, forse un linguaggio che è metalinguaggio per sé (mi chiedo se è possibile).

    
posta mrpyo 17.08.2011 - 21:08
fonte

8 risposte

7

Ruby ha questa capacità, insieme a un'API (forse troppo) dettagliata per modificare le classi al volo.

Puoi avere un'idea di ciò osservando la documentazione delle classi di base di Ruby Classe e Module (Class è una sottoclasse di Module, poiché condivide del codice per la gestione dello spazio dei nomi) e la maggior parte dei libri di Ruby avrà almeno una certa copertura di questo.

    
risposta data 17.08.2011 - 21:18
fonte
7

Diversi linguaggi di programmazione sono progettati in base a diverse filosofie e con diversi tipi di utenti in mente. Alcuni linguaggi trattano le classi (o, più in generale, i tipi) come oggetti di prima classe; altre lingue no.

Le lingue che trattano i tipi come oggetti di prima classe possono essere divise in due gruppi:

  1. Quelli che trattano i tipi come oggetti immutabili in fase di esecuzione (ad es può interrogare, ma non modificare le operazioni valide su un tipo, e il tipi di parametri e valori di ritorno di queste operazioni)
  2. Quelli che trattano i tipi come oggetti regolari in fase di esecuzione (ad esempio, è possibile definire e modificare dinamicamente tipi e operazioni in fase di esecuzione).

Questi ultimi sono particolarmente potenti, anche se queste funzionalità solitamente hanno un costo in termini di prestazioni, perché un runtime deve occuparsi dei tipi e delle operazioni che hai definito dinamicamente.

Per quanto riguarda l'aspetto dell'attuazione: lo chiedi come utente di una lingua o come designer di un linguaggio?

Per un utente di una lingua, l'unica cosa che conta è ciò che ho detto nella parte precedente: un runtime deve occuparsi dei tipi e delle operazioni che hai definito.

Per un progettista di linguaggi, beh, devi progettare questo runtime. Oh, e implementalo, se vuoi che il tuo linguaggio venga usato nel mondo reale.

Non vedo il punto della domanda su come sarebbe la sintassi. La sintassi è, per definizione, specifica della lingua. Se sei un designer di linguaggi, ti suggerisco di prendere la semantica prima di preoccuparti della sintassi.

    
risposta data 17.08.2011 - 21:38
fonte
6

Certo, niente di più semplice che avere una classe sia un oggetto di prima classe:

Object subclass: #MyObject
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'My-Category'

(Smalltalk.)

    
risposta data 17.08.2011 - 22:53
fonte
4

Eli Bendersky ha recentemente pubblicato Python Metaclasses , che discute quella stessa domanda . In esso, inizia con

In Python, everything is an object. And that includes classes. In fact, classes in Python are first-class objects – they can be created at runtime, passed as parameters and returned from functions, and assigned to variables.

Prosegue mostrando alcuni esempi di Python funzionanti sulla creazione di un nuovo Metaclass, che può essere utilizzato per creare nuove classi. Vale la pena leggere.

    
risposta data 17.08.2011 - 23:57
fonte
3

Certamente.

Un esempio specifico: in un approccio basato su prototipi , una classe è effettivamente la stessa cosa di un'istanza di una classe, e può essere semplicemente clonato con nuovi campi aggiunti o sovrascritti per creare nuove istanze.

Un semplice esempio basato su prototipo in Clojure:

(def human
  {:species-name "Human"
   :leg-count 2})

(def john-smith
  (merge human
    {:first-name "John"
     :last-name "Smith"
     :profession "Brewer"}))

(def john-silver
  (merge human
    {:first-name "John"
     :last-name "Silver"
     :leg-count 1
     :profession "Pirate"}))

(:leg-count human) ; class value
=> 2

(:leg-count john-smith) ; inherited value
=> 2

(:leg-count john-silver) ; overridden value
=> 1
    
risposta data 17.08.2011 - 23:30
fonte
3

Le classi sono oggetti in Objective-C, una caratteristica che penso provenga dalle radici Smalltalk di Obj-C. Generalmente non si creano nuovi "oggetti di classe" nel codice; il compilatore lo fa per te. Questo ha senso quando ci pensi: un oggetto di classe è un oggetto che rappresenta una classe; se lo istanziate inviandogli un messaggio +alloc , si finirebbe con un nuovo oggetto della classe rappresentato dall'oggetto classe, non un altro oggetto classe. Quest'ultimo bit ha davvero senso, lo prometto, ma è difficile anche per me seguirlo, quindi ecco un codice per illustrare:

Class foo = [NSString class];        // foo points to the the object
                                     //    representing the NSString class
NSString *bar = [[foo alloc] init];  // bar points to an object of type NSString
int length = [bar length];           // length will be 0 because bar is empty

Nota che sia foo che bar sono oggetti validi a cui possiamo inviare messaggi.

In realtà è possibile aggiungere oggetti di classe in fase di esecuzione, ma lo fai effettuando chiamate direttamente al runtime Objective-C.

    
risposta data 17.08.2011 - 23:13
fonte
1

Questo è più o meno il caso in Ruby.

Object.const_set("Student", Class.new)

Questo è forse il modo più semplice per dimostrare questa funzionalità. Una classe in Ruby è un'istanza di Class. Con più codice puoi aggiungere funzioni e variabili membro.

Spiegato in maggior dettaglio qui

    
risposta data 17.08.2011 - 21:20
fonte
1

Dai un'occhiata a CLOS, il Common Lisp Object System , descritto in particolare in Oggetto- Programmazione programmata in Common Lisp: Guida di un programmatore a CLOS nel 1988. La stessa CLOS è stata influenzata da sistemi di oggetti esistenti come Sapori e CommonLoops .

Il seguente definisce o ridefinisce una classe quando viene chiamato:

(defclass my-class (multiple parent classes)
  ((x :initarg :x)
   (y :initarg :y))) 

Questa è una macro, ma esiste l'equivalente funzionale e si chiama ensure-class , che è più facile da usare quando gli argomenti vengono calcolati. Il valore restituito da questa espressione o da (find-class 'my-class) , è un valore di tipo standard-class . Le classi hanno un metaclass , che è un oggetto che descrive come rappresentare le classi e come accedere ai valori dello slot. Puoi cambiare CLOS per farlo comportarsi come un altro sistema di oggetti grazie al protocollo del metaoggetto , descritto in The Art of the Metaobject protocol (aka AMOP). Ad esempio, potresti fornire un argomento :metaclass che rende i tuoi oggetti serializzati e deserializzati da un database.

Una volta che hai una lezione, puoi installarla:

(make-instance 'my-class :x 10 :y 20)

Si noti che l'argomento della classe a make-instance può essere fornito in fase di runtime. Tutti i valori in Common Lisp hanno una classe:

(class-of 3)
=> #<built-in-class fixnum>

(class-of '(a b c))
=> #<built-in-class cons>

Ma non tutto è necessariamente un oggetto.

CLOS ti consente di definire multimetodi e quindi le funzioni generiche non sono legate a un singolo classe. Ad esempio:

(defgeneric attack (attacker target)

  (:method ((w wizard) (v vampire))
    ;; Vampires do not glitter when exposed to light, they burn.
    (cast-spell w 'sunbath :on v))

  (:method ((w wizard) (d dragon))
    ;; Fall, you fool
    (cast-spell w 'paralysis :on d))

  ;; Dispatch on human here, a superclass of wizard

  (:method ((v vampire) (h human)) (bite v h))
  (:method ((d dragon) (h human)) (ignite d h))

  ;; Dragon duel
  (:method ((d1 dragon) (d2 dragon) (bite d1 d2))

  ;; Default case
  (:method (x y) (punch x y)))

Puoi aggiungere o rimuovere metodi in fase di runtime e specificare anche altri tipi di metodi, come :after , :before o :around metodi:

(defmethod attack :around (attacker target)
  (when (>= (roll-dice (attack-points attacker))
            (defense-points target))
    ;; attack is successful, proceed to actual attack
    (call-next-method)))

Quanto sopra viene eseguito attorno ad ogni attacco e verifica se l'attaccante ha successo, in base al caso e alle caratteristiche di entrambi gli oggetti. L'espressione (call-next-method) viene utilizzata per chiamare il successivo metodo :around meno specifico o il successivo metodo primario più specifico se non esiste un altro metodo :around .

Gli oggetti possono cambiare classe in fase di runtime:

(defmethod bite ((v vampire) (h human))
  (take-hit h (- (bite-attack v) (armor h)))
  (change-class h (infected (class-of h))
                :target-class 'vampire
                :delay-turns 3))

Qui, un vampiro infligge un danno a una procedura guidata che quindi si trasforma in una classe infected-wizard , grazie a (infected (class-of h)) che assicura che tale classe esista creando se necessario (richiede alexandria e più vicino -mop ):

(defun infected (class)
  (ensure-class
   (symbolicate 'infected "-" (class-name class))
   :direct-superclasses (list 'infected class)))

L'istanza di human viene aggiornata con nuove slot: qui, il wizard dovrebbe essere trasformato in un generico vampiro in tre turni.

how would you give this functionality while keeping language consistent?

Cambiare classi e metodi al runtime potrebbe essere problematico, ma il sistema è costruito attorno a un protocollo robusto per gestire le modifiche al runtime, come chiamare i metodi di inizializzazione appropriati quando si modifica la classe di un oggetto, o ricalcolare gli elenchi di precedenza della classe prima che le funzioni generiche vengano chiamate .

... but then you loose type-checking

Il controllo dei tipi viene applicato in fase di esecuzione.

    
risposta data 07.02.2016 - 08:51
fonte

Leggi altre domande sui tag