LO Makro Basic Option ''Optional'' - ''IsMissing'' immer ''False''

Nachtrag

Ich habe mal bei Andrew Pitonyak nachgeforscht zum Thema "Optional" und "IsMissing":

[1] Nach einem optionalen Parameter dürfen nur noch optionale Parameter folgen.

[2] Man kann jeden optionalen Parameter weglassen, auch, wenn auf diesen ein Komma folgt, aber:

BEZUG 1: Das funktioniert nur, wenn die optionalen Parameter vom Typ "Variant" sind.
BEZUG 2: Pitonyak hat dieses Verhalten als BUG (der "IsMissing"-Anweisung) eingestuft und gemeldet.
BEZUG 3: Apache OpenOffice (AOO) Bugzilla fühlt sich nicht zuständig, weil es nichts mit StarBasic zu tun hat.

[3] Das Beispiel in der "LO BASIC-IDE Hilfe" zu "Optional" ist fehlerfrei (BEZUG 4):

+ Es entspricht BEZUG 1.
+ Allerdings wäre es hilfreich, wenn darauf hingewiesen würde, dass auf Grund eines noch bestehenden BUGs nur genau diese Variante (bezüglich der Typ-Deklarationen) funktioniert.

[4] Auf Grund des noch bestehenden BUGs (wenn denn Pitonyak mit seiner Einschätzung richtig liegt) tendiere ich persönlich zu Thomas' Vorschlag "sauberer Programmierung" (keinen optionalen Parameter zwischen zwei Kommas weglassen), da man zu leicht vergessen könnte, dass eine (in allen Nutzungs-Varianten) funktionierende "IsMissing"-Anweisung zwingend von der Typ-Deklaration "Variant" abhängt für alle optionalen Parameter.

Grüße
Hans-Werner

BEZUG 1: Andrew Pitonyak (04.2016 - S.55/56) - http://www.pitonyak.org/OOME_3_0.odt

Optional arguments

You can declare arguments as optional by preceding them with the keyword Optional. All of the arguments following an optional argument must also be optional. Use the IsMissing function to determine if an optional argument is missing.

Listing 30. Optional arguments.

REM Make test calls with optional arguments.
REM Calls with Integer and Variant arguments should yield the same result.
REM Unfortunately, they do not.
Sub ExampleArgOptional()
   Dim s$
   s = "Variant Arguments () => " & TestOpt() & CHR$(10) &_
       "Integer Arguments () => " & TestOptI() & CHR$(10) &_
       "---------------------------------------------" & CHR$(10) &_
       "Variant Arguments (,) => " & TestOpt(,) & CHR$(10) &_
       "Integer Arguments (,) => " & TestOptI(,) & CHR$(10) &_
       "---------------------------------------------" & CHR$(10) &_
       "Variant Arguments (1) => " & TestOpt(1) & CHR$(10) &_
       "Integer Arguments (1) => " & TestOptI(1) & CHR$(10) &_
       "---------------------------------------------" & CHR$(10) &_
       "Variant Arguments (,2) => " & TestOpt(,2) & CHR$(10) &_
       "Integer Arguments (,2) => " & TestOptI(,2) & CHR$(10) &_
       "---------------------------------------------" & CHR$(10) &_
       "Variant Arguments (1,2) => " & TestOpt(1,2) & CHR$(10) &_
       "Integer Arguments (1,2) => " & TestOptI(1,2) & CHR$(10) &_
       "---------------------------------------------" & CHR$(10) &_
       "Variant Arguments (1,3) => " & TestOpt(1,3) & CHR$(10) &_
       "Integer Arguments (1,3) => " & TestOptI(1,3) & CHR$(10)
   MsgBox s, 0, "Optional Arguments of Type Variant or Integer"
End Sub

REM Return a string that contains each argument. If the argument
REM is missing, then an M is used in its place.
Function TestOpt(Optional v1, Optional v2, Optional v3) As String
   TestOpt = "" & IIF(IsMissing(v1), "M", Str(v1)) &_
                  IIF(IsMissing(v2), "M", Str(v2)) &_
                  IIF(IsMissing(v3), "M", Str(v3))
End Function

REM Return a string that contains each argument. If the argument
REM is missing, then an M is used in its place.
Function TestOptI(Optional i1%, Optional i2%, Optional i3%) As String
   TestOptI = "" & IIF(IsMissing(i1), "M", Str(i1)) &_
                   IIF(IsMissing(i2), "M", Str(i2)) &_
                   IIF(IsMissing(i3), "M", Str(i3))
End Function

You can omit any optional arguments. Listing 30 demonstrates two functions that accept optional arguments. The functions are the same except for the argument types. Each function returns a string containing the argument values concatenated together. Missing arguments are represented by the letter “M” in the string. Although the return values from TestOpt and TestOpt1 should be the same for the same argument lists, they are not (see Figure 27). This is a bug.

TIP: The IsMissing function returns incorrect results for variables that are not of type Variant when the missing argument is followed by a comma.

Variant Arguments () => MMM
Integer Arguments () => MMM

Hallo Hans-Werner,

ich denke, du erwartest da zuviel von LibO-Basic. Die optionalen Parameter sind ja nützlich, aber das Thema ist offensichtlich bei der Programmierung nicht in allen Konsequenzen durchdacht, es funktioniert wohl nur, wenn bei den optionalen Parametern alle letzten fehlen. Das Thema, fehlende optionale Parameter durch Auslassen eines Eintrags zwischen den Kommas zu signalisieren,scheint einfach nicht berücksichtigt zu sein. Dass das beim Typ Variant funktioniert, halte ich eher für einen sekundären Effekt, weil da wegen der fehlenden Typisierung andere Logiken anspringen.
Die Doku ist ja wohl eher aufgrund von Tests des funktionierenden Systems entstanden und nicht durch die Entwickler erstellt oder sogar als Basis für die Programmierung verwendet worden, also ist sie nur begrenzt zuverlässig.
Ich habe gesucht nach einer BugMeldung mit "IsMissing", aber da nichts gefunden, ich weiß also nicht einmal, ob Andrew Pitonyak das seiner Ansicht nach fehlerhafte Verhalten überhaupt als Bug gemeldet hat, und sehe daher auch keine Kommentare dazu, die vielleicht zeigen würden, was Entwickler dazu meinen.
Nachdem das Auslassen eines Parameters durch Weglassen eines EIntrags zwischen den Kommas offenbar nur zufällig bei nicht typisierten Variablen klappt, muss man einfach akzeptieren, dass man optionale Parameter nur "von hinten nach vorne weglassen kann". Das heißt, dass man diese Option nur verwendet, wenn es eine klare Reihenfolge gibt, in der die Parameter benötigt werden, oder, was wahrscheinlich der Hauptfall ist, für einen einzigen Parameter, wo das überhaupt kein Problem ist.
Damit stütze ich auch Thomas' Argumentation: verwende die optionalen Parameter in Fällen, wo handfest über den fehlenden Parameter eine eindeutige Festlegung getroffen wird (z.B. "fehlt Höhe" -> Höhe = Breite, also Quadrat, Kreis usw.); in komplizierteren Fällen ist es sicher besser, über besondere Übergabewerte wie negative Zahlen u.ä. genauer zu signalisieren, was gewünscht wird, als nur durch die 0-1-Information eines vorhandenen oder fehlenden Parameters, wenn es denn überhaupt sinnvoll ist, die Entscheidung in dem betreffenden Unterprogramm zu treffen.

Gruß

Gerhard

Hallo Gerhard,

da ich grundsätzlich mit Deinen Aussagen übereinstimme, nur ( relativ ;-)) ) kurze Kommentierungen:

ich denke, du erwartest da zuviel von LibO-Basic. Die optionalen Parameter sind ja nützlich, aber das Thema ist offensichtlich bei der Programmierung nicht in allen Konsequenzen durchdacht, es funktioniert wohl nur, wenn bei den optionalen Parametern alle letzten fehlen. Das Thema, fehlende optionale Parameter durch Auslassen eines Eintrags zwischen den Kommas zu signalisieren,scheint einfach nicht berücksichtigt zu sein.

Dann sollte man so eine Anwendung ("[...] fehlende optionale Parameter durch Auslassen eines Eintrags zwischen den Kommas [...]") nicht in der LO BASIC-IDE Hilfe auch noch als Beispiel bringen oder zumindest deutlich darauf hinweisen, dass es ein Sonderfall ist - und diesen ganzen Thread hätte es überhaupt nicht gegeben, denn dann hätte ich gleich von dieser Art Anwendung "die Finger gelassen":

Optional (in Anweisung Function) [Laufzeit]
Ermöglicht es, ein Argument, das einer Function übergeben wird, als optionales Argument zu definieren.
Siehe auch: IsMissing
Syntax:
Function MeineFunktion(Text1 As String, Optional Arg2, Optional Arg3)
Beispiel:
Result = MeineFunktion("Hier", 1, "Dort") ' alle Argumente vorhanden.
Result = MeineFunktion("Test",1) ' zweites Argument fehlt.
Siehe auch Beispielprogramme.

Wo ist eigentlich das Problem, das obige Beispiel zu korrigieren um zukünftige Missverständnisse, Fehlanwendungen (und unnötige Threads) zu vermeiden ?

Dass das beim Typ Variant funktioniert, halte ich eher für einen sekundären Effekt, weil da wegen der fehlenden Typisierung andere Logiken anspringen.

Ohne Kenntnis des Source-Codes kann ich dazu keine fundierte Aussage machen, aber das kann natürlich so sein ...

Ich habe gesucht nach einer BugMeldung mit "IsMissing", aber da nichts gefunden, ich weiß also nicht einmal, ob Andrew Pitonyak das seiner Ansicht nach fehlerhafte Verhalten überhaupt als Bug gemeldet hat, und sehe daher auch keine Kommentare dazu, die vielleicht zeigen würden, was Entwickler dazu meinen.

Doch, hat er als BUG bezeichnet ...

"[...] You can omit any optional arguments. Listing 30 demonstrates two functions that accept optional arguments. The functions are the same except for the argument types. Each function returns a string containing the argument values concatenated together. Missing arguments are represented by the letter “M” in the string. Although the return values from TestOpt and TestOpt1 should be the same for the same argument lists, they are not (see Figure 27). This is a bug. [...]"

http://www.pitonyak.org/OOME_3_0.odt (Seite 55)

... und auf einen bestehenden Issue hingewiesen, der wohl nicht von ihm selbst ist:

"[...] As of version 1.0.3.1, IsMissing will fail with Optional parameters if the type is not Variant and the missing optional parameter is represented by two consecutive commas. I first investigated this behavior after speaking with Christian Anderson [ca@ofs.no]. This is issue 11678 in issuezilla. [...]"

http://www.pitonyak.org/AndrewMacro.odt (Seite 399)

Dieser Issue ( https://bz.apache.org/ooo/show_bug.cgi?id=11678 ) ist, wenn ich das richtig verstanden habe, wegen "nicht Zuständigkeit im Sande verlaufen":

"[...] Comment 1: Misfiled, the framewwork/ scripting deals with the langauge independent scripting framework, not Star Basic: [...]"

Mehr habe ich allerdings dazu auch nicht gefunden, aber auch nichts Gegenteiliges ...

Nachdem das Auslassen eines Parameters durch Weglassen eines EIntrags zwischen den Kommas offenbar nur zufällig bei nicht typisierten Variablen klappt, muss man einfach akzeptieren, dass man optionale Parameter nur "von hinten nach vorne weglassen kann". Das heißt, dass man diese Option nur verwendet, wenn es eine klare Reihenfolge gibt, in der die Parameter benötigt werden, oder, was wahrscheinlich der Hauptfall ist, für einen einzigen Parameter, wo das überhaupt kein Problem ist.
Damit stütze ich auch Thomas' Argumentation: verwende die optionalen Parameter in Fällen, wo handfest über den fehlenden Parameter eine eindeutige Festlegung getroffen wird (z.B. "fehlt Höhe" -> Höhe = Breite, also Quadrat, Kreis usw.); in komplizierteren Fällen ist es sicher besser, über besondere Übergabewerte wie negative Zahlen u.ä. genauer zu signalisieren, was gewünscht wird, als nur durch die 0-1-Information eines vorhandenen oder fehlenden Parameters, wenn es denn überhaupt sinnvoll ist, die Entscheidung in dem betreffenden Unterprogramm zu treffen.

Sehe ich doch auch so (s.u. [4]) ...

Grüße
Hans-Werner

BEZUG 2: Pitonyak hat dieses Verhalten als BUG (der
"IsMissing"-Anweisung) eingestuft und gemeldet.

Hint: Nur weil irgend jemand irgend etwas als Bug *bezeichnet*, bedeutet
das noch lange nicht, dass das ein Bug *ist*.

Es ist allerdings richtig dass die Dokumentationen bezüglich sowohl
OPTION als auch ISMISSING jeweils sehr mager ist, und die *genauen*
'Gebrauchsbedingungen' nicht ausreichend darstellen. /Ich/ stehe auf
Thomas' Seite, dass das Auslassen eines Parameters nie vorgesehen war,
und daher ein undokumentiertes Feature darstellt, das außerdem auch nur
mit dem Typen VARIANT sauber funktioniert.

[3] Das Beispiel in der "LO BASIC-IDE Hilfe" zu "Optional" ist
fehlerfrei (BEZUG 4):

[4] Auf Grund des noch bestehenden BUGs (wenn denn Pitonyak mit seiner
Einschätzung richtig liegt) tendiere ich persönlich zu Thomas' Vorschlag
"sauberer Programmierung" (keinen optionalen Parameter zwischen zwei
Kommas weglassen), da man zu leicht vergessen könnte, dass eine (in
allen Nutzungs-Varianten) funktionierende "IsMissing"-Anweisung zwingend
von der Typ-Deklaration "Variant" abhängt für alle optionalen Parameter.

Meine Meinung aufgrund langjähriger Erfahrung ist, dass optionale
Parameter nur im alleräußersten Notfall verwendet werden sollten, und
zwar nicht wegen dem von dir angesprochenen Problem, sondern aus
wartungstechnischen Gründen. Wenn du in 3 Monaten oder 5 Jahren dein
Programm umschreiben musst, bist du dir selber dankbar, wenn du es von
vornherein so geschrieben hast, dass du *dann* auch schnell wieder
hinein findest. Und das ist bei einer klar vorgegebenen
Parameterstruktur deutlich einfacher als bei einer wirren, bei der man
z. B. erst mal lange überlegen muss, was denn nun ursprünglich erlaubt
gewesen sein soll und was nicht, usw.

Bei einigen wenigen Parametern würde ich lieber konstant einen
Defaultwert übergeben, und bei vielen Parametern macht es eh mehr Sinn,
zu einem Array u. ä. zu greifen.

Wolfgang