В Scala, как бы вы объявили статические данные внутри функции?

Во многих ситуациях я обнаруживаю, что мне нужно создавать долгоживущие значения внутри области функций, и нет необходимости в том, чтобы эти данные находились в области класса/объекта.

Например,

object Example {

   def activeUsers = {
       val users = getUsersFromDB  // Connects to the database and runs a query.
       users.filter(_.active)
   }
}

Выше переменная users находится в правильной области, но она будет выполнять запрос базы данных каждый раз, когда вызывается функция activeUsers.

Чтобы этого избежать, я мог перемещать переменную users вне области действия:

object Example {
   val users = getUsersFromDB  // Connects to the database and runs a query

   def activeUsers = {
       users.filter(_.active)
   }
}

Но это делает его доступным и для других функций.

В противном случае я мог бы создать отдельный объект для включения функции:

object Example {

   object activeUsers {
       val users = getUsersFromDB  // Connects to the database and runs a query.

       def apply() = {
           users.filter(_.active)
       }
   }
}

Но это включает в себя более шаблонный код, использование другого объекта и незначительные синтаксические нечеткости, связанные с apply.

  • Поддерживается ли что-то подобное на уровне языка?
  • Если нет, есть ли какой-либо стандартный метод, который вы используете в этой ситуации?

Ответ 1

Другим вариантом будет использование закрытия:

object Example {
   val activeUsers = {
       val users = getUsersFromDB
       () => users.filter(_.active)
   }
}

Описание

activeUsers - это переменная типа Function1[Unit, ...your filter result type...] (или мы можем записать этот тип как (Unit => ...your filter result type...), который является тем же самым), то есть эта переменная хранит функцию. Таким образом, вы можете использовать его позже способом, неотличимым от функции, например activeUsers()

Мы инициализируем эту переменную блоком кода, где объявляем переменную users и используем ее внутри анонимной функции () => users.filter(_.active), следовательно, это замыкание (поскольку оно имеет связанную переменную users).

В результате мы достигаем ваших целей: (1) activeUsers выглядит как метод; (2) users вычисляется один раз; и (3) filter работает при каждом вызове.

Ответ 2

Расширение FunctionXX - еще один способ достижения цели; он может иметь преимущество в обеспечении лучшей документации. Оба типа параметров и тип возвращаемого значения видны в первой строке объявления:

val activeUser = new Function0[List[String]] {
  val users = getUsersFromDB
  def apply = users filter (_.active)
}