2013-10-03 41 views
17

Tôi muốn sử dụng hàm tạo riêng tư trong macro. Ví dụ này là một số nguyên dương, nhưng mẫu cơ bản có thể không chỉ được sử dụng cho các loại số khác như số chẵn, mà còn có các loại có nguồn gốc từ chuỗi như địa chỉ email hoặc tên thư mục. Bằng cách làm cho hàm tạo riêng tư, người dùng bị từ chối cơ hội tạo ra một loại bất hợp pháp. Tôi có mã sau đây:Sử dụng hàm tạo riêng tư trong macro

object PosInt 
{ 
    import language.experimental.macros 
    import reflect.runtime.universe._ 
    import reflect.macros.Context 

    def op(inp: Int): Option[PosInt] = if (inp > 0) Some(new PosInt(inp)) else None 

    def apply(param: Int): PosInt = macro apply_impl 

    def apply_impl(c: Context)(param: c.Expr[Int]): c.Expr[PosInt] = 
    { 
    import c.universe._ 
    param match { 
     case Expr(Literal(i)) if (i.value.asInstanceOf[Int] > 0) => 
     case Expr(Literal(i)) if (i.value.asInstanceOf[Int] == 0) => c.abort(c.enclosingPosition, "0 is not a positive integer") 
     case Expr(Literal(i)) => c.abort(c.enclosingPosition, "is not a positive integer")  
     case _ => c.abort(c.enclosingPosition, "Not a Literal") 
    }  
    reify{new PosInt(param.splice)}  
    } 
} 

class PosInt (val value: Int) extends AnyVal 

Tuy nhiên nếu tôi tạo PosInt Constructor riêng, mặc dù Macro biên dịch như dự kiến ​​tôi sẽ gặp lỗi nếu cố sử dụng macro. Tôi không thể làm thế nào để xây dựng cây biểu hiện bằng tay, nhưng tôi không chắc chắn nếu điều đó sẽ giúp anyway. Có anyway tôi có thể làm điều này?

Bạn vẫn không thể sử dụng hàm tạo riêng tư ngay cả khi PosInt không phải là một lớp giá trị. Tôi sẽ chấp nhận câu trả lời không sử dụng lớp giá trị. Những bất lợi của các lớp học giá trị là họ có được loại tẩy xoá. Các lớp cộng mà tôi quan tâm như các tập hợp con của các tọa độ 2d không thể được triển khai như các lớp giá trị. Tôi không thực sự quan tâm đến số nguyên dương, tôi chỉ sử dụng chúng như một chiếc giường thử nghiệm đơn giản. Tôi đang sử dụng Scala 2.11M5. Scala 2.11 sẽ có thêm tính năng quasiquotes. Tôi đã không làm việc ra làm thế nào để sử dụng, quasiquotes nào được nêu ra, như tất cả các tài liệu tại thời điểm này trên chúng dường như giả sử một quen thuộc với Macro Paradise, mà tôi không có.

+3

Tôi không nghĩ rằng điều này là có thể, nhưng tôi vừa viết một [bài đăng blog] nhanh chóng (http://meta.plasm.us/posts/2013/10/03/natural-vampires/) với một phác thảo của lý luận của tôi và một cách tiếp cận thay thế bằng cách sử dụng các phương pháp ma cà rồng. –

+2

Tôi cũng không nghĩ rằng điều này là có thể. Có vẻ như cách duy nhất để đáp ứng trực tiếp các yêu cầu của câu hỏi là tạo ra một nhà máy có tên điên rồ, tên của nó sẽ đẩy lùi con người khỏi việc gọi nó. –

Trả lời

0

Tôi không phải là chuyên gia, nhưng tôi cho rằng tôi sẽ quay ... Trong Java, phạm vi của một hàm tạo riêng được giới hạn ở cùng một lớp ... do đó đối tượng PosInt sẽ cần được chuyển vào phạm vi của cùng một lớp mà từ đó nó được gọi. Với điều đó đã nói, tôi đã tìm thấy một bài viết cho thấy hai cách bạn có thể giữ cho đối tượng không được thừa kế @http://www.developer.com/java/other/article.php/3109251/Stopping-Your-Class-from-Being-Inherited-in-Java-the-Official-Way-and-the-Unofficial-Way.htm

Mô tả này sử dụng từ khóa "cuối cùng" trong tuyên bố lớp để ngăn không cho nó được kế thừa. Đó là cách "chính thức". Cách "không chính thức" là làm cho hàm tạo riêng tư, nhưng thêm một phương thức tĩnh công khai trả về một đối tượng của lớp ...

Vâng, tôi biết, đó là một câu hỏi cũ ... nhưng nó vẫn chưa được trả lời . Bạn không bao giờ biết khi nào một câu hỏi cũ sẽ là hit hàng đầu trong kết quả tìm kiếm của ai đó ...

2

Thật không may cho những gì bạn đang cố gắng đạt được, macro không hoạt động theo cách này. Họ chỉ thao túng AST lúc biên dịch. Dù kết quả cuối cùng là gì, nó luôn luôn là một cái gì đó bạn có thể đã viết theo nghĩa đen trong Scala (không có macro).

Vì vậy, để hạn chế các giá trị có thể có của PosInt, bạn sẽ cần kiểm tra thời gian chạy ở đâu đó, hoặc trong hàm dựng công khai hoặc phương thức nhà máy trên đối tượng đồng hành.

Nếu trường hợp ngoại lệ thời gian chạy không ngon miệng cho bạn, sau đó một cách tiếp cận có thể sẽ là:

  • Tận dụng tin constructor trên lớp.
  • Cung cấp (ví dụ) phương thức create trên đối tượng đồng hành trả về Option[PosInt] (hoặc Try[PosInt] hoặc một số loại lựa chọn khác cho phép bạn thể hiện "lỗi" khi đối số nằm ngoài phạm vi).
  • Cung cấp phương thức apply trên đối tượng đồng hành tương tự như ví dụ của bạn, trong đó xác minh tại thời gian biên dịch đối số nằm trong phạm vi và sau đó trả về cây biểu thức chỉ cần gọi create(x).get.

Gọi là .get trên tùy chọn có thể chấp nhận trong trường hợp này vì bạn chắc chắn rằng nó sẽ không bao giờ là None.

Nhược điểm là bạn phải lặp lại kiểm tra hai lần: một lần tại thời gian biên dịch và một lần khi chạy.

Các vấn đề liên quan