La clase como objeto de primera clase

7

¿Podría una clase ser un objeto de primera clase? En caso afirmativo, ¿cómo sería la sintaxis para crear dinámicamente nuevas clases?

Para restringir la pregunta: ¿cómo daría esta funcionalidad mientras mantiene el lenguaje consistente?

Por ejemplo, cómo crear una referencia para un nuevo tipo. También hace referencia al objeto de primera clase y luego usa algo como esto:

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

Bueno, esto podría causar problemas, por lo que puede forzar al usuario a usar referencias de tipo de Objeto para clases creadas dinámicamente, pero luego perderá la comprobación de tipos.

Creo que crear una sintaxis concisa para este es un problema interesante que resolver podría conducir a un mejor diseño del lenguaje, tal vez un idioma que sea el metalenguaje en sí mismo (me pregunto si esto es posible).

    
pregunta mrpyo 17.08.2011 - 21:08

8 respuestas

7

Ruby tiene esta capacidad, junto con una API (quizás también) detallada para modificar clases sobre la marcha.

Puede probar esto mirando la documentación de las clases principales de Ruby. Class y Module (La clase es una subclase de Módulo, ya que comparten algún código para la gestión del espacio de nombres), y la mayoría de los libros de Ruby tendrán al menos cierta cobertura de esto.

    
respondido por el jimwise 17.08.2011 - 21:18
7

Los diferentes lenguajes de programación están diseñados de acuerdo con diferentes filosofías y con diferentes tipos de usuarios en mente. Algunos idiomas tratan las clases (o, más generalmente, los tipos) como objetos de primera clase; otros idiomas no.

Los idiomas que tratan los tipos como objetos de primera clase se pueden dividir en dos grupos:

  1. Aquellos que tratan los tipos como objetos inmutables en tiempo de ejecución (es decir, usted puede consultar, pero no modificar las operaciones válidas en un tipo, y la tipos de parámetros y valores de retorno de estas operaciones)
  2. Aquellos que tratan los tipos como objetos regulares en tiempo de ejecución (es decir, puede defina y modifique dinámicamente los tipos y operaciones en tiempo de ejecución).

Los últimos son particularmente poderosos, aunque estas capacidades generalmente tienen un costo de rendimiento, porque el tiempo de ejecución debe ocuparse de los tipos y operaciones que ha definido dinámicamente.

Con respecto a cómo se vería la implementación: ¿Está preguntando esto como usuario de lenguaje o como diseñador de lenguaje?

Para un usuario de idioma, lo único que importa es lo que dije en la parte anterior: un tiempo de ejecución debe ocuparse de los tipos y operaciones que ha definido.

Para un diseñador de idiomas, bueno, tienes que diseñar dicho tiempo de ejecución. Ah, e implementarlo, si desea que su idioma se utilice en el mundo real.

No veo el punto de la pregunta sobre cómo sería la sintaxis. La sintaxis es, por definición, específica del idioma. Si eres diseñador de idiomas, te sugiero que aprendas la semántica antes de preocuparte por la sintaxis.

    
respondido por el pyon 17.08.2011 - 21:38
6

Claro, nada más simple que tener una clase sea un objeto de primera clase:

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

(Smalltalk.)

    
respondido por el Frank Shearar 17.08.2011 - 22:53
4

Eli Bendersky publicó recientemente Metaclasses de Python , que trata esa misma pregunta . En ella, comienza con

  

En Python, todo es un objeto . Y eso incluye clases. En   De hecho, las clases en Python son objetos de primera clase, se pueden crear   en tiempo de ejecución, pasado como parámetros y devuelto desde funciones, y   asignado a las variables.

Continúa mostrando algunos ejemplos funcionales de Python para crear una nueva Metaclass, que se puede usar para crear nuevas clases. Vale la pena leerlo.

    
respondido por el Cyclops 17.08.2011 - 23:57
3

Ciertamente.

Un ejemplo específico: en un enfoque basado en prototipos , una clase es en realidad lo mismo que una instancia de una clase, y simplemente se puede clonar con nuevos campos agregados o sobrescritos para crear nuevas instancias.

Un ejemplo simple basado en un prototipo en 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
    
respondido por el mikera 17.08.2011 - 23:30
3

Las clases son objetos en Objective-C, una característica que creo que proviene de las raíces Smalltalk de Obj-C. Por lo general, no creas nuevos "objetos de clase" en el código; El compilador hace eso por ti. Eso tiene sentido cuando lo piensas: un objeto de clase es un objeto que representa una clase; si lo creas enviándole un mensaje +alloc , terminarás con un nuevo objeto de la clase representada por el objeto de clase, no otro objeto de clase. Lo último realmente tiene sentido, lo prometo, pero es difícil incluso para mí seguirlo, así que aquí hay un código para ilustrar:

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

Tenga en cuenta que tanto foo como bar son objetos válidos a los que podemos enviar mensajes.

En realidad, es posible agregar objetos de clase en tiempo de ejecución, pero lo haces haciendo llamadas al tiempo de ejecución de Objective-C directamente.

    
respondido por el Caleb 17.08.2011 - 23:13
1

Eso es más o menos el caso en Ruby.

  

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

Esta es quizás la forma más sencilla de demostrar esta función. Una clase en Ruby es una instancia de Class. Con más código puede agregar funciones y variables de miembro.

Explicado con más detalle aquí

    
respondido por el thorsten müller 17.08.2011 - 21:20
1

Eche un vistazo a CLOS, el Common Lisp Object System , descrito especialmente en Object- Oriented Programming in Common Lisp: una guía del programador para CLOS en 1988. El mismo CLOS fue influenciado por sistemas de objetos existentes como Flavors y CommonLoops .

Lo siguiente define o redefine una clase cuando se le llama:

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

Esta es una macro, pero el equivalente funcional existe y se llama ensure-class , que es más fácil de usar cuando se calculan los argumentos. El valor devuelto por esta expresión, o por (find-class 'my-class) , es un valor del tipo standard-class . Las clases tienen una metaclase , que es un objeto que describe cómo representar las clases y cómo acceder a los valores de la ranura. Puede cambiar CLOS para que se comporte como otro sistema de objetos gracias al protocolo de metaobjetos , descrito en Art of the Metaobject protocol (también conocido como AMOP). Por ejemplo, podría proporcionar un argumento :metaclass que haga que sus objetos se serialicen y deserialicen desde una base de datos.

Una vez que tengas una clase, puedes crear una instancia:

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

Tenga en cuenta que el argumento de clase para make-instance puede darse en tiempo de ejecución. Todos los valores en Common Lisp tienen una clase:

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

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

Pero no todo es necesariamente un objeto.

CLOS le permite definir multimethods y, por lo tanto, funciones genéricas no están vinculadas a una sola clase. Por ejemplo:

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

Puede agregar o eliminar métodos en tiempo de ejecución y especificar también otro tipo de métodos, como los métodos :after , :before o :around :

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

Lo anterior se ejecuta alrededor de cada ataque y verifica si el atacante tiene éxito, basándose en el azar y las características de ambos objetos. La expresión (call-next-method) se usa para llamar al siguiente método menos específico :around o al siguiente método primario más específico si no existe otro método :around .

Los objetos pueden cambiar la clase en tiempo de ejecución:

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

Aquí, un vampiro inflige daño a un asistente que luego se convierte en una clase infected-wizard , gracias a (infected (class-of h)) que asegura que tal clase existe al crearla si es necesario (requiere alexandria y más cerca -mop ):

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

La instancia de humano se actualiza con nuevas ranuras: aquí, se supone que el asistente se convertirá en un vampiro genérico en tres turnos.

  

¿Cómo darías esta funcionalidad mientras mantienes el lenguaje consistente?

El cambio de clases y métodos en tiempo de ejecución puede ser problemático, pero el sistema se basa en un protocolo robusto para tratar los cambios en tiempo de ejecución, como llamar a los métodos de inicialización apropiados al cambiar la clase de un objeto, o volver a calcular las listas de precedencia de clases antes de llamar a las funciones genéricas. .

  

... pero luego pierdes la comprobación de tipos

La verificación de tipo se aplica en tiempo de ejecución.

    
respondido por el coredump 07.02.2016 - 08:51

Lea otras preguntas en las etiquetas