Je peux appeler n'importe quelle méthode sur Nil et cela me semble mal

14

J'ai passé beaucoup de temps à déboguer un script récemment, et quand j'ai finalement trouvé le problème, c'était à cause du code qui ressemblait à ceci:

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

Il s'est avéré que le problème était avec cela $!.bar, qui aurait dû être soit $!barou $.bar. J'ai compris.

Mais pourquoi cela ne meurt-il pas ?

En regardant plus en détail, il semble que la question ici est que je suis en train d'appeler une méthode (inexistante) barsur $!, qui à ce stade est Nilparce qu'il n'y a pas eu d'erreurs.

Et il semble que je puisse réellement appeler n'importe quelle méthode que je veux Nilet ils reviennent tous en silence Nil, y compris des trucs comme Nil.this-is-a-fake-methodet Nil.reverse-entropy(123).

Est-ce une fonctionnalité? Si oui, quelle est la justification?

jja
la source

Réponses:

13

C'est prévu et documenté, oui. Le titre Nilest "Absence de valeur ou défaillance bénigne", et la documentation de la classe mentionne

Toute méthode faisant appel à Nilune méthode qui n'existe pas, et par conséquent, toute opération d'indexation, réussira et retournera Nil.

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

Le synopsis 2 déclare "Tout appel de méthode non défini sur les Nilretours Nil, de sorte que se Nilpropage vers le bas les chaînes d'appels de méthode. De même, toute opération d'indexation sur les Nilretours Nil", de sorte que l'intention semble être de permettre des expressions comme $foo.Bar()[0].Baz()sans exiger de vérification Nilà chaque étape, ou spéciale "appel de méthode et opérateurs d'indexation.

Hobbs
la source
4
En effet. Et il y a déjà longtemps: github.com/rakudo/rakudo/commit/174727377f (décembre 2013) Voir aussi la spéculation associée: design.raku.org/S02.html#Nil
Elizabeth Mattijsen
1
@ElizabethMattijsen ah, je pense que S02 a la réponse. "Tout appel de méthode non défini sur les Nilretours Nil, de sorte que se Nilpropage vers le bas les chaînes d'appels de méthode." Il est donc prévu que vous puissiez faire $foo.Bar().Baz().Blah()sans avoir besoin de tests nil à chaque étape ou d'un ?.type spécial d'opérateur (tant que vous gérez correctement nil à la fin). Je vais le modifier, merci.
hobbs
1
Un Nilqui ne fait pas d'erreur et renvoie simplement plus Nilobtient une utilisation assez étendue dans Obj-C et les cadres NS où il est utilisé d'une manière similaire - permet des appels chaînés qui continuent à passer sur Nil. Je ne connais pas les pénalités de performances exactes des exceptions et de leur capture, mais je suppose que le Nilchaînage peut être plus efficace, mais moins flexible.
user0721090601
1
(mais c'est une supposition totalement mal informée, donc je suis heureux que lizmat ou jnthn me dise que je me trompe et que je dois me taire :-))
user0721090601
2
La Nilclasse a une méthode FALLBACK docs.raku.org/language/typesystem#index-entry-FALLBACK_(method) , qui renvoie Nil. Fondamentalement, juste avant qu'une exception ne soit levée car une méthode est introuvable, une vérification FALLBACKest effectuée et appelée si disponible.
Elizabeth Mattijsen
5

Cette question (et la réponse de Hobbs) m'a également fait me sentir ... mal à l'aise: j'ai finalement trouvé https://docs.raku.org/language/traps : cela explique que l'attribution Nilproduit généralement une valeur différente Any. L'interaction REPL de base suivante le démontre également:

> 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 différence entre =et :=est couverte ici: https://docs.raku.org/language/containers#Binding ))

... donc l'idée générale est que la `` valeur absente ou l'échec bénin '' est beaucoup moins répandue dans le code normal que je (et peut-être vous aussi?) craignais soudainement: cela se produit cependant lorsque vous allez travailler avec regex correspond directement et dans votre situation accidentelle particulière liée à l'invocation directe de méthodes sur $!.

Cela m'a fait prendre conscience de la différence entre Nilet Any, et la justification fournie était plus logique pour moi.

chromis
la source
2
Nildéfinit un conteneur à son état par défaut. Vous pouvez modifier la valeur par défaut. my $foo is default(42) = 5; $foo = Nil; say $foo; # 42
Brad Gilbert