Mit ad vissza?

Pythno:

def a():
    try:
        return True
    finally:
        return False

PHP

function a()
{
    try { return true; }
    finally { return false; }
}

C#

bool A()
{
    try { return true; }
    finally { return false; }
)

Java

boolean a()
{
    try { return true; }
    finally { return false; }
}

JavaScript

function a() {
    try { return true; }
    finally { return false; }
}

Ruby

def a()
  begin
    return true
  ensure
    return false
  end
end

ObjectPascal/Delphi:

function A(): boolean;
begin
    try
        result := true;
    finally
        result := false;
    end;
end;

Hozzászólások

Ez tetszik! :) Futtatás nélkül trükkösebb megmondani, de eddig ezekből a dokumentációkból mind ugyanazt a választ olvasom ki.
Python

Javascript: #1 és #2

C#

Java

Ja, hát ja. Szerintem a legelfogadhatóbb az, amit a C# csinál, hogy le se engedi fordítani azt, hogy ha megpróbálunk kiugrani a finally statementből, illetve az (Object)Pascalé is egy elég egyértelműen érthető, mert ott igazából egy result variable van.

Az összes többinél viszont agyfasz és nagyon könnyen nem kívánt side-effectbe futhat az ember (igazából kollégám talált egy ilyet egy pythonos kódban tegnap, onnan jött az érdeklődés).

Csak egy példa Java-ban:

class Main {

  static boolean A()
  {
    for (int i = 0; i < 10; i++)
    {
      try
      { 
        return true; 
      }
      finally 
      { 
        continue;
      }
    }

    return false;
  }

  public static void main(String[] args) {
    System.out.println(A() ? "TRUE" : "FALSE");
  }
}

Remek példák arra is, hogy miért ne használjunk vezérlési szerkezeteket (Exception-t főként beleértve).

Attól, hogy rámondjuk, hogy nem vezérlési szerkezet, attól még az.

A throw, try-catch és finally mindegyike megváltoztatja a futási sorrendet, hasonló, mint a goto (és label) vagy az if vagy a ciklus, ...

Akkor nem vezérlési szerkezet, ha az adott utasítás lefutása után a következővel folytatódik minden esetben a futás.

Scala alatt az ajánlott visszatérési mód, hogy azt adja vissza, ami az utolsó kifejezés értéke volt.
Pl.

def a(): Boolean = {
  try {
    true
  } finally {
    false
  }
}

Ilyenkor true-t ad vissza, de lehet return-t is használni, ami nem ajánlott.

def a(): Boolean = {
  try {
    return true
  } finally {
    return false
  }
}

Ilyenkor false-t ad vissza.

Ennél azért komplexebb a probléma. Az utolsó visszatérési mód Pascalban elég jól átjön a kódból. Viszont a többi nyelvben nagyon nem egyértelmű a kódból az, hogy mi történik. "Jó" látni, hogy a sok false ellenére van egy true is, csak hogy teljes legyen a káosz :)

Teljesen félrevezető, hogy egyszer visszatérünk a methodból/függvényből, aztán van még lehetőség arra, hogy eltérítsük a program futását, ld. a fentebb posztolt Javas példa, ahol bár returnolunk, végigfut a ciklus. Ilyen szempontból a legszimpatikusabb az, amit a C# csinál, hogy nem enged kilépni semmilyen módon (break, continue, return, goto, akármi) a finally blokkból, le se fordul az a kód. Illetve a Pascal megoldása még bőven az elfogadható kategóriában van számomra, mert egyértelmű a kódból, hogy mi történik.

Persze, komplex a probléma, mert nem tudjuk, tudhatjuk, hogy melyik az erősebb. Ezért rosszak ezek a megoldások (try-catch, return, ...)

Az első Scala-s példa kvázi ua., mint a Pascal-os, de ott sem egyértelmű, hogy melyik lesz az eredmény.
Számomra egyik sem az elfogadható kategória, esetleg a C#-os még elmegy.
A legszimpatikusabb az lenne, ha van a kódban try, catch, finally, throw (vagy más vezérlési szerkezet), akkor nem fordulna le ;-)

Szerintem a java-way most a legjobb ... Nem meglepo, hogy ugy mukodik ahogy. A tobbi szar.

A szinfalak mogott ez tortenik es sebesseget tekintve ez a legjobb megoldas:

boolean a()
{
    boolean ret;
    try { ret = true; }
    finally { ret = false; }

    return ret;
}

Ilyen hulyeseg eseten megis mit varnal el?

  • Ne forduljon! - Nem jo, a True/False helyen siman lehetnenek valtozok is, azok erteke meg nem derulne ki forditaskor.
  • Runtime hiba! - Ez sem teljesen jo, siman lehet, hogy mindket esetben (try es finally) ertelmes erteket adna a hivo fele.
  • Legyen True! - Hogyne, akkor a finally nem futna le, pedig az direkt azert van, hogy mindig lefusson. Ez talan a legrosszabb valasztas.
  • Legyen False! - Ez sem tokeletes, de meg mindig az a legjobb, ha bele tudsz szolni a finallynal a visszateresi ertekbe, hogy megse True legyen.
  • Legyen undefined! - Tipikus C mukodes :) innentol barmi tortenhet.

When you tear out a man's tongue, you are not proving him a liar, you're only telling the world that you fear what he might say. -George R.R. Martin

Azt hiszem nem jól értelmezted a "Nem fordul" esetet. Mindegy, hogy mit ad vissza, ha return van, vagy más (pl. break, continue, return, goto, akármi) ami kiugrana a finallyból, akkor nem fordul.

return változó;
esetén nem kell tudnia a fordítónak, hogy mi lesz majd a változóban, csak azt, hogy ott return van, ami nem lehet.

Ilyen hulyeseg eseten megis mit varnal el?

- Leginkább azt, amit a C# tesz: le se forduljon.

Ne forduljon! - Nem jo, a True/False helyen siman lehetnenek valtozok is, azok erteke meg nem derulne ki forditaskor.

Legyenek. De az akkor egyértelmű és explicit ki van jelentve, hogy ott az fog történni. Az, hogy fordításkor a fordító számára mi derül ki, az igazából ilyen szempontból irreleváns.

Szerkesztve: 2019. 11. 29., p - 21:45

Az exception-öket mindig is jó ötletnek tartottam, pláne finally-val (sarcasm, Sheldon), úgyhogy ebben sem látok semmi rosszat/furcsát, inkább azt kérdezném, mi történik, ha a finally-ban is van exception? Van ilyenkor egy finally-finally?

Lusta vagyok mindet kiprobalni, elkerhetem a valaszokat?

Persze:

C#-ban fordítási hiba, nem enged semmiféle módon kiugrani a finally kontextjéből (se goto, se break, se continue, semmi. Egyedül exceptiont lehet dobni onnan)

Minden máshol false lesz a végeredmény. Ebből a pascal még érthető, mert ott a kódból egyértelműen látszik, hogy nem egy return van, hanem egy result variable, a többinél viszont kurvára nem egyértelmű, hogy valójában az történik a háttérben, úgyhogy szerintem az egy szar megoldás.

Rust-ban megnéztem, ott nem tudtam a fenti példát megcsinálni. Valakinek esetleg sikerült hasonlót?

A hiba felterjesztést végző ? operátor itt Result<T,E>-vel tér vissza, tehát kell neki Ok(T) vagy Err(E).
https://doc.rust-lang.org/std/result/

Minden nyelvnél lehet élni ezzel a "csalással". ;-)

Általában bonyolultabbnak tartják a "csalós" módszert az Exception dobással szemben.
Egyik esetben:
- minden bemenetre ad vissza értéket a fv,
- mindig tudod, hogy mit fog visszaadni,
- mindig tudod, hogy mi lesz a következő lépés,
- mindig könnyen meg tudod állapítani, hogy adhat-e hibát a fv,
- mindig könnyen meg tudod állapítani, hogy kezelve van-e a hiba.

Ezek vajon az egyszerűbbre igazak vagy a bonyolultabbra? ;-)

Lehet, csak nem mindig érdemes. Anno nézegettem pl. a Quake II forráskódját. Tulajdonképp próbált néhol OOP lenni C-ben, de ahogy megcsinálta... hát meh. Akkor már lett volna inkább C++. Ugyanígy: oké, van pattern matching C#-ban is már valamilyen szinten, de ha most mindent így kellene megcsinálni, elég pain in the ass lenne. Egyszerűen nem így találták ki a nyelvet.

Meg igazából a "minden hibát le kell kezelni" című híres mondásnál is sokan elfelejtik idézni azt a részt, hogy "a megfelelő helyen". És az közel sem biztos, hogy a megfelelő hely az ott lesz, ahol kiváltódik az exception.

Ez így igaz, nem mindig érdemes egyszerű (könnyen érthető, karbantartható, tesztelhető) kódot írni. Van amikor jobb, ha gyorsan elkészül, van amikor jobb, ha nagyobb a teljesítménye.

Ahhoz, hogy a "megfelelő helyen" le tudd kezelni a hibát, ahhoz elsődlegesen tudnod kellene, hogy ott lehet-e hiba, milyen hiba lehet, miért lehet ott hiba, miként kellene lekezelni. Ha ezekről semmit nem tudsz, sőt, nem is akarsz róla tudni, akkor elég nehéz ott lekezelni.