Puedo llamar a cualquier método en cero y esto se siente mal

14

Pasé un tiempo considerable depurando un script recientemente, y cuando finalmente encontré el problema fue por un código que se veía así:

class Foo {
    has $.bar;
    method () {
        # do stuff
        $!.bar;
    }
}

Resultó que el problema era con eso $!.bar, que debería haber sido $!baro $.bar. Entiendo esto.

¿ Pero por qué no muere esto ?

En cuanto a esto con más detalle, parece que el problema aquí es que estoy tratando de llamar a un método (inexistente) barde $!que en este momento es Nilporque no ha habido ningún error.

Y parece que realmente puedo llamar a cualquier método que quiera Nily todos regresan en silencio Nil, incluyendo cosas como Nil.this-is-a-fake-methody Nil.reverse-entropy(123).

¿Es esta una característica? Si es así, ¿cuál es la razón?

jja
fuente

Respuestas:

13

Está destinado y documentado, sí. El título Niles "Ausencia de un valor o una falla benigna", y la documentación de la clase menciona

Cualquier llamada a Nilun método que no exista y, en consecuencia, cualquier operación de suscripción, tendrá éxito y regresará Nil.

say Nil.ITotallyJustMadeThisUp;  # OUTPUT: «Nil␤» 
say (Nil)[100];                  # OUTPUT: «Nil␤» 
say (Nil){100};                  # OUTPUT: «Nil␤»

La sinopsis 2 dice "Cualquier llamada de método indefinido en las Nildevoluciones Nil, de modo que se Nilpropague hacia abajo en las cadenas de llamadas de método. Del mismo modo, cualquier operación de subíndice en las Nildevoluciones Nil", por lo que la intención parece ser permitir expresiones como $foo.Bar()[0].Baz()sin requerir controles Nilen cada paso, o "Nil-safe especial" "llamada a método y operadores de suscripción.

hobbs
fuente
44
En efecto. Y hace ya bastante tiempo también: github.com/rakudo/rakudo/commit/174727377f (diciembre de 2013) Véase también la especulación asociada: design.raku.org/S02.html#Nil
Elizabeth Mattijsen
1
@ElizabethMattijsen ah, creo que S02 tiene la respuesta. "Cualquier llamada de método indefinido en Nildevoluciones Nil, de modo que Nilpropaga cadenas de llamadas de método". Por lo tanto, está destinado a que pueda hacerlo $foo.Bar().Baz().Blah()sin necesidad de pruebas nulas en cada paso o un ?.tipo especial de operador (siempre que maneje correctamente nil al final). Lo editaré en, gracias.
Hobbs
1
A Nilque no produce errores y solo arroja más, Nilobtiene un uso bastante extenso en Obj-C y NS Frameworks, donde se usa de manera similar: permite llamadas encadenadas que continúan pasando a Nil. No sé las penalizaciones de rendimiento exactas de las excepciones y la captura, pero supongo que el Nilencadenamiento puede ser más eficiente, aunque menos flexible.
user0721090601
1
(pero esa es una suposición completamente desinformada, así que estoy feliz de que lizmat o jnthn me digan que estoy equivocado y que necesito
callarme
2
La Nilclase tiene un método FALLBACK docs.raku.org/language/typesystem#index-entry-FALLBACK_(method) , que devuelve Nil. Básicamente, justo antes de que se produzca una excepción porque no se pudo encontrar un método, se realiza una verificación FALLBACKy se llama si está disponible.
Elizabeth Mattijsen
5

Esta pregunta (y la respuesta de hobbs) también me hizo sentir ... incómodo: finalmente encontré https://docs.raku.org/language/traps : explica que la asignación Nilproduce un valor diferente, por lo general Any. La siguiente interacción básica REPL también demuestra esto:

> my $foo = Nil
(Any)

> $foo.bar
No such method 'bar' for invocant of type 'Any'
  in block <unit> at <unknown file> line 1

> my $bar := Nil
Nil

> $bar.baz
Nil

((la diferencia entre =y :=se cubre aquí: https://docs.raku.org/language/containers#Binding ))

... así que la idea general es que el 'valor ausente o falla benigna' está mucho menos extendido en el código regular de lo que yo (¿y quizás usted también?) de repente temía: sin embargo, ocurre cuando va a trabajar con expresiones regulares coincide directamente y en su situación accidental particular relacionada con la invocación directa de métodos $!.

Esto me hizo más consciente de la diferencia entre Nily Any, y la justificación proporcionada tenía más sentido para mí.

Chromis
fuente
2
Nilestablece un contenedor a su estado predeterminado. Puede cambiar el valor predeterminado. my $foo is default(42) = 5; $foo = Nil; say $foo; # 42
Brad Gilbert