2015-09-02 11 views
6

Bối cảnh

Tôi có Map[String,String] giá trị cấu hình. Tôi muốn trích xuất một loạt các khóa và cung cấp các thông báo lỗi có ý nghĩa nếu bất kỳ thông báo nào bị thiếu. Ví dụ:Sử dụng tính hợp lệ với | @ | ở Scalaz

val a = Map("url"->"http://example.com", "user"->"bob", "password"->"12345") 

Nói rằng tôi muốn chuyển đổi này vào một lớp học trường hợp:

case class HttpConnectionParams(url:String, user:String, password: String) 

Bây giờ, tôi chỉ đơn giản là có thể sử dụng một vòng lặp for để trích xuất các giá trị:

for(url <- a.get("url"); 
    user <- a.get("user"); 
    password <- a.get("password")) yield { 
    HttpConnectionParams(url,user,password) 
} 

Để nhận được Option[HttpConnectionParams]. Điều này là tốt đẹp và sạch sẽ, ngoại trừ nếu tôi nhận được một None sau đó tôi không biết những gì đã mất tích. Tôi muốn cung cấp thông tin đó.

Xác thực với Scalaz

Nhập scalaz. Tôi đang sử dụng phiên bản 7.1.3.

Từ những gì tôi đã có thể đặt lại với nhau (một tài liệu tham khảo tốt là here) Tôi có thể sử dụng disjunctions:

for(url <- a.get("url") \/> "Url must be supplied"; 
    user <- a.get("user") \/> "Username must be supplied"; 
    password <- a.get("password") \/> "Password must be supplied") yield { 
    HttpConnectionParams(url,user,password) 
} 

này là tốt đẹp bởi vì bây giờ tôi nhận được một thông báo lỗi, nhưng điều này là bởi vì nó railway oriented dừng lại ở lần thất bại đầu tiên. Nếu tôi muốn nhận tất cả các lỗi thì sao? Hãy sử dụng xác nhận và xây dựng applicative (aka "| @ |"):

val result = a.get("url").toSuccess("Url must be supplied") |@| 
      a.get("username").toSuccess("Username must be supplied") |@| 
      a.get("password").toSuccess("Password must be supplied") 

result.tupled match { 
    case Success((url,user,password)) => HttpConnectionParams(url,user,password) 
    case Failure(m) => println("There was a failure"+m) 
} 

Câu hỏi

này làm những gì tôi mong đợi, nhưng tôi có một số câu hỏi về cách sử dụng:

  • Is có một cách dễ dàng để sử dụng thay thế cho scalaz cho trường hợp sử dụng này? Tôi không muốn mở hộp của pandora và giới thiệu scalaz nếu tôi không phải.
  • Một lý do tôi không muốn sử dụng scalaz là thực sự rất khó để tìm ra việc cần làm nếu bạn không, như tôi, biết toàn bộ khuôn khổ. Ví dụ: danh sách các thư mục bạn cần để làm cho mã trên hoạt động như thế nào? import scalaz._ bằng cách nào đó đã không làm việc cho tôi. [1] Làm thế nào tôi có thể hình dung điều này từ tài liệu API?
  • Có cách nào ngắn gọn hơn để thể hiện trường hợp sử dụng hợp lệ không? Tôi tình cờ gặp tôi cho đến khi tôi đến một cái gì đó mà làm việc và tôi không có ý tưởng nếu có khác, cách tốt hơn để làm điều tương tự trong scalaz.

[1] Sau nhiều kỳ vọng tôi đã đến bộ nhập khẩu này cho trường hợp sử dụng ứng dụng. Hy vọng rằng đây sẽ giúp ai đó:

import scalaz.std.string._ 
import scalaz.syntax.std.option._ 
import scalaz.syntax.apply._ 
import scalaz.Success 
import scalaz.Failure 
+0

"Một lý do tôi không muốn sử dụng scalaz là thực sự rất khó để biết phải làm gì nếu bạn không, như tôi, biết toàn bộ khuôn khổ" - Tôi sẽ không nói trực tiếp với Tony Morris;). Scalaz có thể không phải là tách trà của mọi người; có một đường cong học tập dốc có liên quan để hiểu nó nếu bạn không phải là người thuyết phục FP hardcore và/hoặc không quen thuộc với Haskell (phải mất rất nhiều cảm hứng từ ngôn ngữ đó). Đó là một thư viện mạnh mẽ nếu bạn biết phải làm gì với nó - Tôi có ấn tượng rằng bạn đang gõ nó vì bạn không thể bị làm phiền để học nó đầy đủ - đó không phải lỗi của Scalaz –

+0

Một giải pháp thay thế cho Scalaz là Mèo. Một trong những mục tiêu của nó là cởi mở hơn và thân thiện với người dùng hơn so với Scalaz về tài liệu và ví dụ, mặc dù tôi đã tìm thấy cộng đồng Scalaz sẵn sàng giúp đỡ với bất kỳ vấn đề nào tôi đã có. –

+0

Có, đồng nghiệp đề nghị Mèo nhưng ở phiên bản 0.2, có sẵn sàng để mọi người thường sử dụng không? –

Trả lời

13

Bạn có thể làm nhiều hơn nữa này một chút độc đáo bằng cách định nghĩa một phương pháp helper và bỏ qua .tupled bước bằng cách sử dụng .apply:

import scalaz._, Scalaz._ 

def lookup[K, V](m: Map[K, V], k: K, message: String): ValidationNel[String, V] = 
    m.get(k).toSuccess(NonEmptyList(message)) 

val validated: ValidationNel[String, HttpConnectionParams] = (
    lookup(a, "url", "Url must be supplied") |@| 
    lookup(a, "username", "Username must be supplied") |@| 
    lookup(a, "password", "Password must be supplied") 
)(HttpConnectionParams.apply) 

Ngoài ra, xin đừng xấu hổ đến sử dụng import scalaz._, Scalaz._. Chúng ta đều làm điều đó và nó chỉ là tốt trong phần lớn các trường hợp. Bạn luôn có thể quay lại và tinh chỉnh nhập khẩu của mình sau.Tôi cũng vẫn đứng bởi this answer Tôi đã viết nhiều năm trước đây - bạn không nên cảm thấy như bạn cần phải có một sự hiểu biết toàn diện về Scalaz (hoặc mèo) để có thể sử dụng các phần của nó một cách hiệu quả.

+2

Chỉ cần nhận thấy rằng đây không thực sự là câu trả lời cho câu hỏi đầu tiên của bạn, nhưng hiện tại không có thiên vị sai, tích lũy loại disjunction trong thư viện chuẩn, vì vậy trừ khi bạn muốn cuộn của riêng bạn, mèo hoặc Scalaz (hoặc Scalactic) là đặt cược tốt nhất của bạn. –

+0

Và bạn đã trả lời một câu hỏi mà tôi đã có mà không có tôi hỏi nó, đó là việc sử dụng các lớp danh sách không trống (ValidationNel, vv). Điều này là để tránh một kết nối đơn giản của nhiều thông báo lỗi cho bất kỳ ai theo dõi. Cảm ơn, điều này khá hữu ích. –

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