2010-10-22 9 views
5

Trong Rails, người ta có thể sử dụng:Có tương tự trong Scala cho phương thức "trả về" của Rails không?

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

Tránh phải làm điều này:

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

Tôi nghĩ rằng cách cũ là sạch và ít lặp đi lặp lại. Tôi thấy bản thân mình tạo ra phương pháp này trong các dự án Scala của tôi:

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

Tôi biết rằng nó là của tiện ích phần nào hạn chế do xu hướng của các đối tượng là bất biến, nhưng ví dụ làm việc với Lift, sử dụng phương pháp này trên lớp Mapper hoạt động khá tốt.

Có tương tự Scala để "trả lại" mà tôi không biết? Hoặc, liệu có cách tương tự để làm điều này trong Scala có thành ngữ hơn không?

Trả lời

5

có thể không thực sự cải thiện nhiều về những gì bạn đã viết. Như bạn đã chỉ ra một cách chính xác, Scala thành ngữ có khuynh hướng ưu tiên những vật không thay đổi được, vì vậy loại vật dụng này bị hạn chế sử dụng.

Ngoài ra, như một lớp lót, thực sự không phải là điều khó khăn khi thực hiện bản thân nếu bạn cần!

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

Cảm ơn bạn đã trả lời. Tôi đã tìm ra điều này có lẽ là trường hợp nhưng sau khi học cách sử dụng Option/Box với map/flatMap/foreach, tôi đã háo hức tìm ra cách Scala để tiếp cận vấn đề. – Collin

3

tôi sẽ làm:

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) 
+0

Cảm ơn bạn đã gửi câu trả lời, nhưng đây thực sự là một cách tốt nhất để trả về giá trị tùy ý, nhưng để cho phép một số thao tác được thực hiện trên nó thông qua hàm mức cao hơn mà không gây ô nhiễm phạm vi bên ngoài tài liệu tham khảo. – Collin

+0

Tôi không thấy điểm của bạn. Làm thế nào có thể một người (first_name = "Collin", last_name = "VanDyck") gây ô nhiễm phạm vi bên ngoài? – pedrofurla

+0

BTW cách tiếp cận này không yêu cầu - nhưng cho phép - vars thay vì vals. – pedrofurla

6

phương pháp của bạn có vẻ tốt đẹp, mặc dù tôi thường làm điều này bằng cách thêm một phương pháp để tác dụng phụ, trong đó có thể bao gồm thay đổi trạng thái nội bộ (hay còn những thứ như 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) 

Chỉnh sửa: chỉ trong trường hợp nó không rõ ràng thế nào điều này sẽ có ích trong ví dụ ban đầu:

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

chỉnh sửa: thay đổi tên của phương pháp để "hiệu ứng". Tôi không biết tại sao tôi không đi theo cách đó trước đây - bên hiệu ứng hiệu ứng, không phải bên có hiệu lực cho việc đặt tên.

+0

Tôi đã phải đọc cái này một vài lần để hoàn toàn mò mẫm nó. Giải pháp tuyệt vời, tôi chắc chắn sẽ sử dụng nó, cảm ơn. – Collin

+0

Tôi chủ yếu sử dụng nó như vậy: 'list.map (_._ 2 <7) .effect (println) .filter (_._ 3 ==" yes ")'. Việc có thể đặt trường hoặc thực hiện các cập nhật khác chỉ là một tai nạn hạnh phúc. (Vâng, không hoàn toàn là một tai nạn, nhưng đó không phải là trường hợp sử dụng bình thường của tôi.) –

+0

Tôi nghĩ Ruby gọi nó là .tap - phải nói tôi thích tên của bạn tốt hơn! –

2

Tôi không hiểu tại sao Vasil xóa câu trả lời của riêng mình, nhưng tôi thích nó rất nhiều (đó là chính xác những gì tôi đã đi để đề nghị):

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

Một trong những tính năng người đã bị yêu cầu là khả năng tự động nhập nội dung nào đó. Nếu có thể, bạn có thể thực hiện điều này:

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

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

Hiện tại, cũng không có trong lộ trình của Scala. Nhưng một số người yêu cầu một cái gì đó như thế bây giờ và một lần nữa.

+0

Không biết về địa phương, cảm ơn. Ví dụ đầu tiên bạn đưa ra là những gì tôi đang cố gắng tránh (val người ..; ...; người). Tôi nghĩ rằng lý do mà tôi đã tìm kiếm điều này là bởi vì tôi đã đến Scala từ Ruby, nơi chúng tôi nhận được để Object # instance_eval khá nhiều :) – Collin

1

Một đề xuất khác sẽ sử dụng số forward pipe operator từ Scalaz.

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

Sự khác biệt là bạn sẽ phải tự trả lại giá trị, nếu bạn muốn chỉ định giá trị đó.Nếu bạn không cần giá trị trả lại (như trong ví dụ ban đầu của bạn), mọi thứ trở nên dễ dàng hơn:

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

Và ở đó bạn đi.

Tôi miễn cưỡng thực sự sử dụng nó trong mã của riêng mình (mặc dù tôi ủng hộ cách này để làm điều đó - nhưng nó chỉ được ghi lại trong scalaz và có thể khó tìm ra những người khác đang xem mã), vì vậy tôi hy vọng những ví dụ này hoạt động.

Tất nhiên, bạn có thể xác định ‘đường ống tiến và trả về’ của riêng mình bằng cách sử dụng |>.

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) 
1

Có thể tránh lặp lại những tên biến như vậy:

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

Lưu ý rằng điều này chỉ làm việc cho val s. Ngoài ra, locally là một phương pháp Predef giúp tạo các khối chỉ để hạn chế phạm vi biến đổi, mà không chạy afoul của suy luận dấu chấm phẩy của Scala. Điều này giúp cho việc nhập không cho bạn theo cách của bạn khi bạn đã hoàn thành việc khởi tạo người đó.