11

Tôi đã đọc A wreq tutorial:Làm thế nào để sử dụng các thấu kính sáng tác của Haskell bằng cách sử dụng thành phần chức năng với thứ tự các đối số kỳ lạ đó?

Một ống kính cung cấp một cách để tập trung vào một phần của một giá trị Haskell. Đối với ví dụ , loại Response có ống kính responseStatus, trong đó tập trung vào thông tin trạng thái được máy chủ trả về.

ghci> r ^. responseStatus 
Status {statusCode = 200, statusMessage = "OK"} 

Nhà điều hành ^. mất một giá trị như là đối số đầu tiên của nó, một ống kính như thứ hai của mình, và trả về phần giá trị tập trung vào bằng ống kính.

Chúng tôi tạo ống kính sử dụng thành phần chức năng, cho phép chúng tôi dễ dàng tập trung vào một phần của cấu trúc lồng nhau sâu.

ghci> r ^. responseStatus . statusCode 
200 

tôi không thể tìm ra một cách làm thế nào hàm hợp thực hiện với lệnh này của các đối số có thể đối xử với cơ cấu tổ theo thứ tự đó.

Nhìn: r ^. responseStatus . statusCode có thể là r ^. (responseStatus . statusCode) hoặc (r ^. responseStatus) . statusCode.

Trong người đầu tiên nói rằng chúng ta xây dựng một chức năng mà đầu tiên xử lý statusCode (được nó từ các bản ghi Status - như tôi có thể suy ra từ giá trị hiện Status {statusCode = 200, statusMessage = "OK"}), và sau đó vượt qua nó để responseStatus mà phải đối xử với trạng thái phản hồi. Vì vậy, đó là cách khác vòng: trên thực tế, mã trạng thái là một phần của trạng thái phản hồi.

Lần đọc thứ hai cũng không có ý nghĩa với tôi vì nó cũng xử lý mã trạng thái trước.

+3

'statusCode' phải là ống kính chứ không phải bộ chọn trường bản ghi. Tôi đoán họ phải giấu bộ chọn trường và xuất khẩu một ống kính cùng tên; khá khó hiểu nếu bạn hỏi tôi. (Hoặc đã viết một trường hợp Hiển thị tùy chỉnh.) –

+0

@ReidBarton Đó phải là một phần của câu đố. Vì tôi biết rất ít về thấu kính, câu hỏi của tôi cũng là một câu hỏi về ý tưởng cơ bản: làm thế nào để có thể sử dụng thành phần chức năng bình thường để truy cập cấu trúc theo thứ tự ngược lại? (Điều này nhắc tôi tiếp tục đi qua một chút.) –

+3

Đó là cơ bản chính xác. Thấu kính là loại giống như tính toán CPS của một loại để họ, như là một tác dụng phụ, thành phần chức năng lật. –

Trả lời

12

Đọc đúng r ^. responseStatus . statusCoder ^. (responseStatus . statusCode). Điều này chỉ là tự nhiên, vì hàm thành phần trả về một hàm khi được áp dụng cho hai đối số, do đó (r ^. responseStatus) . statusCode phải trả về một hàm, trái ngược với bất kỳ giá trị nào có thể được in ra.

Điều này vẫn để mở câu hỏi tại sao ống kính soạn theo thứ tự "sai". Kể từ khi thực hiện các ống kính là một chút huyền diệu, hãy xem xét một ví dụ đơn giản hơn.

first là một chức năng mà các bản đồ trên các yếu tố đầu tiên của một cặp:

first :: (a -> b) -> (a, c) -> (b, c) 
first f (a, b) = (f a, b) 

không map . first làm gì? first mất một chức năng tác động lên các yếu tố đầu tiên, và trả về một chức năng hoạt động trên một cặp, mà là rõ ràng hơn nếu chúng ta nghĩ giải lao loại theo cách này:

first :: (a -> b) -> ((a, c) -> (b, c)) 

Ngoài ra, nhớ lại các loại map:

map :: (a -> b) -> ([a] -> [b]) 

map có chức năng hoạt động trên phần tử và trả về hàm hoạt động trên danh sách. Bây giờ, f . g hoạt động bằng cách trước tiên áp dụng g và sau đó cho kết quả vào f.Vì vậy, map . first có chức năng hoạt động trên một số loại phần tử, chuyển đổi nó thành một hàm hoạt động theo cặp, sau đó chuyển đổi nó thành một hàm hoạt động trên danh sách các cặp.

(map . first) :: (a -> b) -> [(a, c)] -> [(b, c)] 

firstmap cả biến các chức năng hoạt động trên một phần của một cấu trúc chức năng tác động lên toàn bộ cấu trúc. Trong map . first, toàn bộ cấu trúc cho first trở thành tiêu điểm cho map là gì.

(map . first) (+10) [(0, 2), (3, 4)] == [(10, 2), (13, 4)] 

Bây giờ hãy nhìn vào các loại ống kính:

type Lens = forall f. Functor f => (a -> f b) -> (s -> f t) 

Cố gắng bỏ qua Functor bit cho bây giờ. Nếu chúng tôi hơi nheo mắt, điều này giống như các loại cho mapfirst. Và nó xảy ra để thấu kính cũng chuyển đổi các chức năng tác động lên các phần của cấu trúc thành chức năng tác động lên toàn bộ cấu trúc. Trong chữ ký trên s biểu thị toàn bộ cấu trúc và a biểu thị một phần của nó. Vì chức năng nhập của chúng tôi có thể thay đổi loại a thành b (như được chỉ ra bởi a -> f b), chúng tôi cũng cần tham số t, có nghĩa là "loại s sau khi chúng tôi thay đổi a thành b bên trong".

statusCode là một ống kính có thể chuyển đổi một chức năng diễn xuất trên một Int đến một diễn xuất chức năng trên Status:

statusCode :: Functor f => (Int -> f Int) -> (Status -> f Status) 

responseStatus chuyển đổi một chức năng hoạt động trên một Status đến một chức năng hoạt động trên một Response:

responseStatus :: Functor f => (Status -> f Status) -> (Response -> f Response) 

Loại responseStatus . statusCode theo cùng một mẫu như chúng tôi đã thấy với map . first:

responseStatus . statusCode :: Functor f => (Int -> f Int) -> (Response -> f Response) 

Nó vẫn còn để được nhìn thấy như thế nào chính xác ^. công trình. Nó gắn liền với cơ khí cốt lõi và ma thuật của ống kính; Tôi sẽ không nhắc lại nó ở đây, vì có khá nhiều tác phẩm về nó. Để giới thiệu, tôi khuyên bạn nên xem this onethis one và bạn cũng có thể xem this excellent video.

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