Ruby-Alcance léxico vs herencia
inheritance lexical-scope (4)
Esta es una continuación de esta pregunta original: Usar "::" en lugar de "módulo ..." para el espacio de nombres de Ruby
En la pregunta SO original, aquí está el escenario presentado que todavía tengo problemas para entender:
FOO = 123
module Foo
FOO = 555
end
module Foo
class Bar
def baz
puts FOO
end
end
end
class Foo::Bar
def glorf
puts FOO
end
end
puts Foo::Bar.new.baz # -> 555
puts Foo::Bar.new.glorf # -> 123
¿Puede alguien dar alguna explicación de por qué la primera llamada está devolviendo 555 y por qué la segunda llamada está devolviendo 123?
Puedes pensar en cada aspecto del module Something
, class Something
o def something
como una "puerta de entrada" en un nuevo ámbito. Cuando Ruby está buscando la definición de un nombre al que se ha hecho referencia, primero se ve en el alcance actual (el método, clase o módulo), y si no se encuentra allí, volverá a través de cada uno que contenga "puerta de enlace" y buscará el alcance allí.
En su ejemplo, el método baz
se define como
module Foo
class Bar
def baz
puts FOO
end
end
end
Por lo tanto, al tratar de determinar el valor de FOO
, primero se comprueba la Bar
clase y, como Bar
no contiene un FOO
la búsqueda se mueve hacia arriba a través del "gateway de la class Bar
" hacia el módulo Foo
que contiene el alcance. Foo
contiene un FOO
constante (555) así que este es el resultado que ves.
El método glorf
se define como:
class Foo::Bar
def glorf
puts FOO
end
end
Aquí la "puerta de enlace" es class Foo::Bar
, así que cuando FOO
no se encuentra dentro de Bar
la "puerta de enlace" pasa a través del módulo Foo
y directamente al nivel superior, donde hay otro FOO
(123) que es lo que se muestra .
Observe cómo el uso de la class Foo::Bar
crea una única "puerta de enlace", omitiendo el alcance de Foo
, pero el module Foo; class Bar ...
module Foo; class Bar ...
abre dos "gateways" separados
glorf es un método de la clase Foo, en => [Foo, Module, Object, Kernel, BasicObject]
dentro de ese alcance (es decir, en el módulo predeterminado / principal), se asigna FOO 123
módulo Foo se define como
module Foo
FOO = 555
class Bar
def baz
puts FOO
end
end
end
donde el método baz pertenece a la clase Bar en el módulo Foo => [Bar, Foo, Object, Kernel, BasicObject]
y en ese alcance se asignó a FOO 555
la primera llamada:
puts Foo::Bar.new.baz # -> 555
imprime el resultado de invocar el método baz de una instancia de la clase Foo :: Bar
observe que la definición de Foo :: Bar # baz es en realidad un cierre en FOO . Siguiendo las reglas de alcance de Ruby:
- FOO se busca en el alcance de Foo :: Bar (la clase, no la instancia), no se encuentra,
- FOO se busca en el alcance adjunto Foo (porque estamos dentro de la definición del módulo) y se encuentra allí (555)
la segunda llamada:
puts Foo::Bar.new.glorf # -> 123
imprime el resultado del método de invocación glorf de una instancia de la clase Foo :: Bar
observe que la definición de Foo :: Bar # glorf esta vez también es un cierre en FOO , pero si seguimos las reglas de alcance de ruby notará que el valor cerrado en este momento es :: FOO (alcance máximo del nivel FOO) de la siguiente manera :
- Se busca FOO en el espacio de nombres Foo :: Bar (la clase, no la instancia), no se encuentra
- FOO se busca en el alcance adjunto (''nivel superior'') y se encuentra allí (123)
wow, gran pregunta. La mejor respuesta que puedo encontrar es que en este caso está usando el módulo para definir un espacio de nombres.
Mira esto:
FOO = 123
module Foo
FOO = 555
end
module Foo
class Bar
def baz
puts FOO
end
def glorf3
puts ::FOO
end
end
end
class Foo::Bar
def glorf2
puts Foo::FOO
end
def glorf
puts FOO
end
end
puts Foo::Bar.new.baz # -> 555
puts Foo::Bar.new.glorf # -> 123
puts Foo::Bar.new.glorf2 # -> 555
puts Foo::Bar.new.glorf3 # -> 123
Entonces mi pensamiento es que cuando defines:
module Foo
FOO = 555
end
estás creando FOO
en el espacio de nombres de Foo
. Entonces cuando lo usas aquí:
module Foo
class Bar
def baz
puts FOO
end
end
end
estás en el espacio de nombres de Foo
. Sin embargo, cuando te refieres a ella en:
class Foo::Bar
def glorf
puts FOO
end
end
FOO
proviene del espacio de nombres predeterminado (como se muestra en ::FOO
).