Есть ли в Scala аналог для метода "возврата" в Rails? - PullRequest
5 голосов
/ 22 октября 2010

В Rails можно использовать:

returning Person.create do |p|
  p.first_name = "Collin"
  p.last_name = "VanDyck"
end

Избегая необходимости делать это:

person = Person.create
person.first_name = "Collin"
person.last_name = "VanDyck"
person

Я думаю, что первый способ более чистый и менее повторяющийся.Я создаю этот метод в своих проектах Scala:

def returning[T](value: T)(fn: (T) => Unit) : T = {
  fn(value)
  value
}

Я знаю, что он имеет несколько ограниченную полезность из-за склонности объектов быть неизменными, но, например, работая с Lift, используя этот метод наКлассы Mapper работают довольно хорошо.

Есть ли аналог Scala для "возврата", о котором я не знаю?Или есть в Scala подобный способ, который более идиоматичен?

Ответы [ 6 ]

6 голосов
/ 22 октября 2010

Ваш метод выглядит хорошо, хотя я обычно делаю это, добавляя метод для побочных эффектов, который может включать изменение внутреннего состояния (или также такие вещи, как println):

class SideEffector[A](a: A) {
  def effect(f: (A => Any)*) = { f.foreach(_(a)); a }
}
implicit def can_have_side_effects[A](a: A) = new SideEffector(a)

scala> Array(1,2,3).effect(_(2) = 5 , _(0) = -1)
res0: Array[Int] = Array(-1, 2, 5)

Редактировать: на всякий случай неясно, как это будет полезно в исходном примере:

Person.create.effect(
  _.first_name = "Collin",
  _.last_name = "VanDyck"
)

Редактировать: изменил название метода на «эффект». Я не знаю, почему я не пошел по этому пути раньше - эффект стороны , а не сторона для именования.

5 голосов
/ 22 октября 2010

Не может реально улучшить то, что вы уже написали. Как вы правильно заметили, идиоматическая Scala предпочитает неизменные объекты, так что подобные вещи имеют ограниченное применение.

Кроме того, будучи однострочником, на самом деле не так больно реализовываться, если вам это нужно!

def returning[T](value: T)(fn: T => Unit) : T = { fn(value); value }
3 голосов
/ 22 октября 2010

Я бы сделал:

scala> case class Person(var first_name: String = "", var last_name: String = "")
defined class Person

scala> Person(first_name="Collin", last_name="VanDyck")
res1: Person = Person(Collin,VanDyck)
2 голосов
/ 22 октября 2010

Я не понимаю, почему Василь удалил свой собственный ответ, но мне он очень нравится (именно это я и собирался предложить):

val person = Person.create
locally { import person._
  first_name = "Collin"
  last_name = "VanDyck"
}
person

Один изфункции, о которых люди просили, - это возможность автоматического импорта чего-либо.Если бы это было возможно, то вы могли бы сделать это:

def returning[T](import value: T)(fn: => Unit) : T = { fn; value }

returning(Person.create) {
  first_name = "Collin"
  last_name = "VanDyck"
}

В настоящее время это невозможно, и это не входит в план действий Scala.Но некоторые люди просят что-то подобное время от времени.

1 голос
/ 22 октября 2010

Можно избежать повторения имени переменной следующим образом:

val person = Person.create
locally { import person._
  first_name = "Collin"
  last_name = "VanDyck"
}

Обратите внимание, что это работает только для val с. Кроме того, locally - это метод Predef, который помогает создавать блоки просто для ограничения области видимости, не нарушая точку с запятой в Scala. Это не позволяет импорту помешать вам, как только вы закончите инициализацию человека.

1 голос
/ 22 октября 2010

Другим предложением будет использование оператора forward pipe из Scalaz .

val person = Person.create |> { p =>
  p.firstName = "Collin"
  p.lastName = "VanDyck"
  p // or p.saveMe
}

Разница в том, что вам придется возвращать значение самостоятельно, если вы хотите присвоить его. Если вам не нужно возвращаемое значение (как в первоначальном примере), все будет проще:

Person.create |> { p =>
  p.firstName = "Collin"
  p.lastName = "VanDyck"
  p.save
}

И вот, пожалуйста.

Я неохотно использовал его в своем собственном коде (хотя я и одобряю этот способ - но он документирован только в скалазе, и, возможно, его трудно будет понять другим, кто просматривает код), Я надеюсь, что эти примеры работают.

Конечно, вы можете определить свой собственный «прямой и обратный канал», используя |>.

class ReturningPipe[A](value: A) {
  import Scalaz._
  def |>>[B](f: A => B):A = value.|>(a => { f(a); value})
}
implicit def returningPipe[A](value: A) = new ReturningPipe(value)
...