סוגריים

אז כמו תמיד אחרי שאני מבלה יותר מדי זמן בלדבג משהו טפשי אני הולך לכתוב את זה כאן כדי לא לשכוח וכדי שלפחות נלמד משהו מהסיפור, והיום אנחנו רוצים לדבר על סוגריים וסקאלה. נו, רוצים זאת מילה גדולה, אף אחד לא רוצה לדבר על סוגריים. ובכל זאת לפעמים צריך ובמיוחד כשסוגריים יכולים להיות מבלבלים. נתחיל בקוד-

  @main
  def parens(): Unit =
    val result = if (Random.nextInt() > 0) {
      Try { throw new Exception("20") }
    } else {
      Try { throw new Exception("30") }
    }.recover { err => 30 }

    println(result)

והשאלה מה הערך של result?

בשביל לענות על זה נתחיל עם דוגמת קוד יותר פשוטה ונוריד את ה recover. עכשיו בלוק ה then ובלוק ה else שקולים:

  @main
  def parens(): Unit =
    val result = if (Random.nextInt() > 0) {
      Try { throw new Exception("20") }
    } else {
      Try { throw new Exception("30") }
    }

ו result יהיה שווה לאוביקט Failure שה Exception שלו תלויה בתוצאה של החישוב האקראי. פקודת recover בסקאלה שמופעלת על אוביקט Try משנה אותו מ Failure ל Success עם הערך שמופיע ב recover. הדוגמה הבאה לכן גם צפויה:

  @main
  def parens(): Unit =
    val result = if (Random.nextInt() > 0) {
      Try { throw new Exception("20") }
    } else {
      Try { throw new Exception("30") }
    }

    println(result.recover { err => 30 })

בגלל שלא משנה מה הוגרל result החזיק בכל מקרה Failure, אז הפעלת recover תשנה את הערך ל Success של 30 וזה מה שיודפס:

Success(30)

אבל מה עם קטע הקוד הראשון שהדבקתי? עכשיו אנחנו מבינים מה רציתי שהוא יעשה - רציתי שהוא יפעיל את recover על הערך שחזר מה if, לפני שנשמר ב result. זה לא מה שקרה. בשביל להבין מה כן קרה צריך קודם כל למחוק את הסוגריים המסולסלים:

    val result = if (Random.nextInt() > 0)
                    Try { throw new Exception("20") }
                 else Try { throw new Exception("30")

כדאי לחשוב על ה"בלוק" בסקאלה בתור פרמטר ל if או ל else. לכן אין בעיה לכתוב את השורה בלעדיו ולכן הגירסה הראשונה של הקוד שקולה לגירסה הזו:

val result = if (Random.nextInt() > 0)
                Try { throw new Exception("20") }
                else
                Try { throw new Exception("30") }.recover { _ => 30 }

עכשיו זה ברור - ה recover השפיע רק על בלוק ה else ולא על כל ה if. הקוד החזיר Success כשהמספר האקראי היה 0 או שלילי, ובמספרים חיוביים החזיר Failure. כשמבינים איך זה עובד קל גם לתקן:

  @main
  def parens(): Unit =
    val result = (if (Random.nextInt() > 0) {
      Try { throw new Exception("20") }
    } else {
      Try { throw new Exception("30") }
    }).recover { _ => 30 }

    println(result.recover { err => 30 })