Calocybe: Sichtbarkeit von Funktionen

Hallo Cracks!

Der Titel ist vielleicht nicht 100% passend, aber was besseres ist mir jetzt mal nicht eingefallen.

Bei meiner aktuellen Entwicklung habe ich im Prinzip ein grosses Programm, dass ich zwecks Wiederverwendbarkeit auf mehrere Dateien verteilt habe. Diese Dateien binde ich mittels require ein. Ich verwende nicht use, da ich vor dem Includen noch ein paar globale Variablen stehen habe, die in den Include-Dateien schon bekannt sein sollen. Ich rede von Include-Dateien und nicht von Modulen, da ich keine package-Statements verwende und somit alle Funktionen auf dem top level angesiedelt sind.

So habe ich nun also eine Hauptdatei namens mind.pl und binde dort unter anderem mit
    require "mindtemplate.inc.pl"
eine dieser Include-Dateien ein. Jetzt gibt es da zwei Merkwuerdigkeiten, die ich gerne erklaert bekommen wuerde.

1. Funktionen, die ich in den Include-Dateien definiert habe, scheint der Interpreter nur halb zu kennen. Er findet sie nur, wenn ich sie mit Klammern aufrufe. Lasse ich die Klammern weg, betrachtet er das Statement als "bare word", was je nach Kontext zu verschiedenen Warnungen/Fehlern fuehrt. Der Aufruf
    SetCommonTemplateData();
funktioniert also einwandfrei,
    SetCommonTemplateData;
erzeugt dagegen die Warnung "Useless use of a constant in void context at mind.pl line 202." (und die Funktion wird nicht aufgerufen). Wenn die Funktion Argumente bekommt, wie z.B.
    $err = LoadConfigFile $CONFIG_FILENAME, %CONFIG;
wird die Warnung "Useless use of reference constructor in void context at mind.pl line 266." fuer das %CONFIG erzeugt (vermutlich als sinnloses Statement rechts vom Komma-Operator angesehen) und dann der Fehler "Can't call method "LoadConfigFile" without a package or object reference at mind.pl line 266.". (Die Warnung wird uebrigens schon zur Compile time erzeugt, die Fehlermeldung erst zur Laufzeit.)

2. Ich habe eine Funktion
    sub EvaluateTemplateData(@) { ... }
in einer dieser Include-Dateien, die zwingend eine Referenz auf ein Array haben will. Rufe ich diese von innerhalb dieser Include-Datei mit
    EvaluateTemplateData(@template);
auf, erhalte ich die Fehlermeldung
    Type of arg 1 to main::EvaluateTemplateData must be array (not reference constructor) at mindtemplate.inc.pl line 98, near "@template)"
Also rufe ich nun ohne den Backslash auf, und es geht (scheinbar wird automatisch eine Referenz erzeugt). Nun rufe ich die Funktion aber auch von anderen Dateien aus auf. Wenn ich dort auch nur
    EvaluateTemplateData(@template);
schreibe, wird wirklich das Array uebergeben, nicht eine Referenz darauf. Ist nicht ganz verwunderlich, wenn wie bei 1. festgestellt die Funktionsdefinition offenbar nicht bekannt ist. Als Folge tut die Funktion scheinbar ueberhaupt nichts, es gibt aber auch keine Fehlermeldungen oder Warnungen. Aber warum ueberhaupt darf ich innerhalb der mindtemplate.inc.pl nicht mit @template als Argument aufrufen?

Nun ja, freue mich auf Erleuchtung...

Calocybe

  1. Hallo Calocybe,

    was bin ich froh, daß ich viiiel zu feige bin, solche Probleme überhaupt bekommen zu können ...

    Diese Dateien binde ich mittels require ein. Ich verwende nicht use, da ich vor dem Includen noch ein paar globale Variablen stehen habe, die in den Include-Dateien schon bekannt sein sollen. Ich rede von Include-Dateien und nicht von Modulen, da ich keine package-Statements verwende und somit alle Funktionen auf dem top level angesiedelt sind.

    Ist für mich alles keineswegs schlüssig. Ich verwende generell nur packages, schon alleine weil ich den Überblick behalten will, wer in meinem name space herumsaut.

    Ich kann doch in packages "globale" Variablen definieren und diese von außerhalb mit packagename::variable ansprechen? Ich mache das dauernd - etwa bei einem Modul, der eine Datei parst und das Ergebnis als globalen Hash verfügbar macht. Reicht Dir das nicht?

    1. Funktionen, die ich in den Include-Dateien definiert habe, scheint der Interpreter nur halb zu kennen. Er findet sie nur, wenn ich sie mit Klammern aufrufe.

    Auch da bin ich - als Anwender von vielen verschiedenen Sprachen - viel zu ängstlich.
    Erstens verwende ich generell nur Funktionsaufrufe mit Klammern, weil die eben wie Funktionsaufrufe in allen anderen mir geläufigen Sprachen aussehen.
    Zweitens verwende ich nur Aufrufe ohne den "&"-Operator davor, weil dieses das Prptotyping abschalten würde. Drittens natürlich immer "use strict". Perl ist ein übler Hacker-Slang, aber wenn man alles einschaltet, was die Sprache bietet, dann kann man richtig schöne Sachen damit schreiben (mein Projekt-Quelltext hat knapp 20000 lines of Perl code).

    Nun ja, freue mich auf Erleuchtung...

    Ich kann es verschmerzen, eine Sprache nicht bis ins Letzte auszureizen - mir ist wichtiger, daß meine Programme funktionieren ... ;-)

    1. Hi hi!

      Ist für mich alles keineswegs schlüssig. Ich verwende generell nur packages, schon alleine weil ich den Überblick behalten will, wer in meinem name space herumsaut.

      Den Ueberblick behalte ich schon noch, so gross ist das Programm nicht. Ausserdem kann man durch entsprechende Benennungen die Zugehoerigkeit klarstellen, z.B. dbDoSomeStuff() fuer Routinen, die ein Database backend kapseln. Das ist im Prinzip schon ein objektorientierter Ansatz, es koennte genausogut $db->DoSomeStuff() heissen.

      Ich kann doch in packages "globale" Variablen definieren und diese von außerhalb mit packagename::variable ansprechen? Ich mache das dauernd - etwa bei einem Modul, der eine Datei parst und das Ergebnis als globalen Hash verfügbar macht. Reicht Dir das nicht?

      Ich hab mir das mit den Modulen auch erst vor ein paar Tagen mal durchgelesen, deshalb habe ich das mal noch gelassen. Ausserdem wuerde das ueberhaupt nicht der gedachten Struktur entsprechen. Ich habe eben nur ein grosses Programm, keine einzelnen Module. Die einzelnen Teile sind mehrfach miteinander verwoben, es sind keine in sich abgeschlossenen Teile. Aufgeteilt habe ich das aus zwei Gruenden: Ich muss nicht in einer grossen Datei soviel rumscrollen und ich kann die Einzelteile genausogut in ein anderes Programm includen, das auf einen Grossteil aehnlicher Funktionalitaet zurueckgreifen muss (und genau das habe ich noch vor mir).

      Aber davon mal abgesehen, wuerden die geschilderten Probleme nicht auftreten, wenn ich in jeder Datei noch eine package-Direktive und das Export-Array an den Anfang setzen wuerde?

      Auch da bin ich - als Anwender von vielen verschiedenen Sprachen - viel zu ängstlich.
      Erstens verwende ich generell nur Funktionsaufrufe mit Klammern, weil die eben wie Funktionsaufrufe in allen anderen mir geläufigen Sprachen aussehen.

      Hatte ich am Anfang auch gemacht. Dann habe ich mich ein bisschen an die Perl-Schlampigkeit gewoehnt und die Klammern oft weggelassen. Seit dieser Erfahrung benutze ich sie nun auch wieder konsequent. Uebrigens kann man dadurch auch einige unerwartete Verhaltensweisen ausschliessen:
          DoSomeStuff $arg1, $arg2 return $err;
      ist naemlich was anderes als
          DoSomeStuff($arg1, $arg2) return $err;
      (Ich sage nur Komma-Operator.)

      Zweitens verwende ich nur Aufrufe ohne den "&"-Operator davor, weil dieses das Prptotyping abschalten würde.

      Der Sinn von dem Ding ist mir bis heute nicht klar - und noch dazu sieht's bloed aus.

      Drittens natürlich immer "use strict". Perl ist ein übler Hacker-Slang, aber wenn man alles einschaltet, was die Sprache bietet, dann kann man richtig schöne Sachen damit schreiben (mein Projekt-Quelltext hat knapp 20000 lines of Perl code).

      Perl ist mit Abstand die dreckigste Sprache, die ich kenne. Kein Wunder, wenn man bedenkt, wo das herkommt. Eine Mischung aus awk, sed, sh, c und anderen. Aber es hat eben eine maechtige Stringverarbeitung. Gib mir entsprechende Biblitheken fuer C und ich gehe wieder dort hin.

      Als ich use strict ausprobiert habe, bin ich aber irgendwie gar nicht damit klargekommen. Ich habe keinen Weg gefunden, globale Variablen zu definieren. Da hab ich's eben gelassen. perl -w muss reichen. Aber ich werde diese Problematik am besten mal mit use strict 'refs' testen.

      Ich kann es verschmerzen, eine Sprache nicht bis ins Letzte auszureizen - mir ist wichtiger, daß meine Programme funktionieren ... ;-)

      Eigentlich sind packages ja schon der hoehere Level, das einfache includen der niedrigere. Also hast Du die Sprache doch schon viel mehr ausgereizt als ich, oder?

      Calocybe

      1. Hi,

        z.B. dbDoSomeStuff() fuer Routinen, die ein Database backend kapseln. Das ist im Prinzip schon ein objektorientierter Ansatz, es koennte genausogut $db->DoSomeStuff() heissen.

        ... oder bei mir eben db::do_some_stuff().

        Ausserdem wuerde das ueberhaupt nicht der gedachten Struktur entsprechen. Ich habe eben nur ein grosses Programm, keine einzelnen Module. Die einzelnen Teile sind mehrfach miteinander verwoben, es sind keine in sich abgeschlossenen Teile.

        Am Anfang sieht jedes Programm so aus - bis man sich die Mühe macht, es zu zerlegen ... wenn ich eine triviale Konfigurationsdatei einlesen will, dann ist mir das einen Modul wert - schon allein deshalb, weil es nicht auf derselben Abstraktionsebene liegt wie das Hauptprogramm.

        Aufgeteilt habe ich das aus zwei Gruenden: Ich muss nicht in einer grossen Datei soviel rumscrollen

        Yup - hätte es nicht Mehraufwand beim Installieren für Stefan bedeutet, dann hätte ich das Suchskript in ein Dutzend Dateien zerlegt.
        Meine erwähnten knapp 20000 Perl-Zeilen bestehen aus 20 CGI-Unterverzeichnissen mit jeweils ca. 5-10 packages bzw. Hauptprogrammen.

        und ich kann die Einzelteile genausogut in ein anderes Programm includen, das auf einen Grossteil aehnlicher Funktionalitaet zurueckgreifen muss (und genau das habe ich noch vor mir).

        Und genau das geht eben *nicht* so gut mit einer Include-Datei, bei der Bezeichner mit irgendwas in dem neuen Programm kollidieren könnten, wie mit einer Package, wo definitiv alles disjunkt ist.

        Aber davon mal abgesehen, wuerden die geschilderten Probleme nicht auftreten, wenn ich in jeder Datei noch eine package-Direktive und das Export-Array an den Anfang setzen wuerde?

        Du müßtest den Export-Array halt überall mit "modulname::" prefixen - das sagt Dir der Interpreter, wenn Du ihn mit "use strict" lieb darum bittest. (Wenn nicht, dann greifst Du gnadenlos auf undefinierte Variablen zu ...)

        DoSomeStuff $arg1, $arg2 return $err;
        ist naemlich was anderes als
            DoSomeStuff($arg1, $arg2) return $err;
        (Ich sage nur Komma-Operator.)

        Klar, aber man darf auch redundant klammern, beispielsweise hier den boolean expression ...

        Zweitens verwende ich nur Aufrufe ohne den "&"-Operator davor, weil dieses das Prptotyping abschalten würde.
        Der Sinn von dem Ding ist mir bis heute nicht klar - und noch dazu sieht's bloed aus.

        Ich bin nicht sicher, aber ich denke, in irgendeiner älteren Perl-Version gab es *nur* diese Art des Aufrufs ...

        Drittens natürlich immer "use strict".
        Als ich use strict ausprobiert habe, bin ich aber irgendwie gar nicht damit klargekommen. Ich habe keinen Weg gefunden, globale Variablen zu definieren. Da hab ich's eben gelassen. perl -w muss reichen. Aber ich werde diese Problematik am besten mal mit use strict 'refs' testen.

        Dann präfixe einfach globale Variablen auch innerhalb ihres Moduls mit dem Modulnamen, und fertig ist die Laube. Lokale Variablen mußt Du dann halt alle deklarieren - dafür findest Du aber zuverlässig jeden Tippfehler in Variablennamen.

        Ich wiederum habe beispielsweise "-w" bisher nicht benutzt ...

        Eigentlich sind packages ja schon der hoehere Level, das einfache includen der niedrigere. Also hast Du die Sprache doch schon viel mehr ausgereizt als ich, oder?

        Naja, ich habe halt das zuerst genommen, was ich aus Turbo-Pascal kannte ... (UNITED/XY auf meiner Homepage sind 33xyz lines in compilerbedingten 64 Modulen)