Đọc đúng r ^. responseStatus . statusCode
là r ^. (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)]
first
và map
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 map
và first
. 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 one và this one và bạn cũng có thể xem this excellent video.
'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.) –
@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.) –
Đó 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. –