在Scala中是否存在用于Rails“返回”方法的模拟?

时间:2010-10-22 12:52:00

标签: ruby-on-rails scala scala-2.8

在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 个答案:

答案 0 :(得分:6)

你的方法看起来很好,虽然我通常通过添加副作用的方法来做到这一点,这可以包括改变内部状态(或者像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"
)

编辑:将方法的名称更改为“效果”。我不知道为什么我之前没有这样做 - 侧面效果,而不是效果的命名。

答案 1 :(得分:5)

无法真正改善你已经写过的东西。正如你非常正确地指出的那样,惯用的Scala倾向于支持不可变对象,所以这种东西的用途有限。

另外,作为一个单行,如果你需要它,实现自己并不是那么痛苦!

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

答案 2 :(得分:3)

我愿意:

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)

答案 3 :(得分:2)

我不明白为什么Vasil删除了他自己的答案,但我非常喜欢(这正是我要提出的建议):

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的路线图。但有些人一次又一次地要求这样的事情。

答案 4 :(得分:1)

另一个建议是使用forward pipe operator中的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
}

然后你去。

我不愿意在我自己的代码中真正使用它(尽管我喜欢这种做法 - 但它只是在scalaz中记录,并且很难找到其他人看代码),所以我希望这些例子能够奏效。

您当然可以使用|>定义自己的“前进和返回管道”。

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)

答案 5 :(得分:1)

可以避免重复变量名称,如下所示:

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

请注意,这仅适用于val。此外,locally是一个Predef方法,它有助于创建块以限制变量范围,而不会与Scala的分号推断相冲突。一旦您完成初始化人员,这将阻止导入阻止您。