2017-10-13 26 views
9

Tôi đang đọc Purescript theo ví dụ và nhận phần giới thiệu đơn vị Reader. Ví dụ như sau:Tìm hiểu về trình đơn Reader

createUser :: Reader Permissions (Maybe User) 
createUser = do 
    permissions <- ask 
    if hasPermission "admin" permissions 
    then map Just newUser 
    else pure Nothing 

Phần khó hiểu đối với tôi là chức năng ask. Chữ ký là:

ask :: forall r. Reader r r 

Có vẻ như nếu nó tạo ra một đầu đọc ra khỏi không khí mỏng

Khi tôi đang đọc về State đơn nguyên, nó có khái niệm tương tự với chức năng get của nó. Và văn bản được giải thích:

trạng thái được triển khai dưới dạng đối số hàm ẩn bởi hàm tạo dữ liệu của đơn vị nhà nước, vì vậy không có tham chiếu rõ ràng nào để truyền xung quanh.

Tôi đoán này là chìa khóa, và điều tương tự đang xảy ra ở đây với Reader, nhưng tôi không hiểu tại sao nó hoạt động ...

Khi ví dụ trên được điều hành qua runReader, làm thế nào giá trị được cung cấp có đột nhiên xuất hiện như là kết quả của ask không? Tài liệu Haskell cho số ask nói: Truy xuất môi trường đơn lẻ. Nhưng sự nhầm lẫn của tôi là từ nơi? Cách tôi nhìn thấy nó, một giá trị được chuyển đến runReader, được lưu trữ ở đâu đó và để có được nó - bạn gọi ask ... nhưng điều đó không có ý nghĩa.

Trong khi ví dụ này là Purescript, tôi đoán bất kỳ người nào biết chữ Haskell cũng sẽ có thể trả lời, do đó thẻ Haskell.

+2

'Đọc b' là một trình bao bọc quanh' a -> b'. Vì vậy, 'ask :: forall a. Đọc a' là cuối cùng chỉ là một giá trị của loại 'forall a. a -> a', với một wrapper xung quanh nó. –

Trả lời

8

Hiện tại tôi không có môi trường PureScript, vì vậy tôi sẽ cố gắng trả lời từ góc nhìn Haskell và hy vọng nó sẽ giúp ích.

Một đọc thực sự chỉ là một 'wrapper' xung quanh một chức năng, vì vậy khi bạn nhận được một Reader r r, bạn thực sự chỉ có được một độc giả từ r để r; nói cách khác, một hàm r -> r.

Bạn thể triệu tập các chức năng ra khỏi không khí mỏng, bởi vì, nếu bạn là một Platon, tôi cho rằng họ luôn tồn tại ...

Khi bạn sử dụng do ký hiệu, bạn 'bên trong đơn nguyên' , do đó, ngữ cảnh r là ẩn. Nói cách khác, bạn gọi hàm trả về giá trị r và khi bạn sử dụng mũi tên <-, bạn chỉ cần nhận bối cảnh đó.

+2

Được thăng hạng. Có lẽ không có giá trị gì khi cách triệu hồi một chức năng «r -> r' từ không khí mỏng như thế để giải thích câu thần chú' id'. (Và 'id' là hàm _only_ như vậy, nhờ tham số.) –

+0

Ok, vậy trong trường hợp của tôi Reader là một trình bao bọc xung quanh Quyền -> Có thể là Người dùng. Khi tôi chạy 'runReader createUser permissions', làm thế nào' ask' trong phần thân của 'createUser' biết trả về cùng' các điều khoản' mà tôi đã truyền cho 'runReader'? Tôi chắc chắn câu hỏi là hết sức vô nghĩa khi tôi nhìn vào điều này tất cả sai ... Nhưng hãy cố gắng giúp tôi unbend não của tôi. – kaqqao

+1

@kaqqao Loại 'Quyền đọc (Có thể là Người dùng)' chỉ là 'trình bao bọc' trên 'Quyền -> (Có thể là Người dùng) ', do đó toàn bộ giá trị' createUser'' của bạn thực sự là một hàm (nhưng hàm là các giá trị) điều đó thật tuyệt). Khi bạn gọi 'runReader', bạn phải vượt qua không chỉ' createUser', mà còn là giá trị cho 'r' - trong trường hợp này là giá trị' Permissions'. 'runReader' sau đó gọi hàm được bọc với giá trị' Permissions' bạn đã truyền nó. HTH. –

1

Bạn có thể thuyết phục bản thân rằng nó hoạt động bằng cách thực hiện một vài thay thế. Đầu tiên hãy nhìn vào chữ ký của createUser. Hãy "cuộn" định nghĩa của Reader:

createUser :: Reader Permissions (Maybe User) 
{- definition of Reader -} 
createUser :: ReaderT Permissions Identity (Maybe User) 

Loại ReaderT chỉ có một nhà xây dựng dữ liệu: ReaderT (r -> m a), có nghĩa createUser là một thuật ngữ để đánh giá một giá trị kiểu ReaderT (Permissions -> Identity (Maybe User)). Như bạn thấy, nó chỉ là một chức năng được gắn thẻ với ReaderT.Nó không phải tạo ra bất cứ thứ gì ngoài không khí mỏng, nhưng sẽ nhận được giá trị loại Permissions khi chức năng đó được gọi.

Bây giờ, hãy nhìn vào dòng bạn đang gặp sự cố. Bạn biết rằng các ký hiệu do chỉ là cú pháp đường, và các biểu hiện:

do permissions <- ask 
    if hasPermission "admin" permissions 
    then map Just newUser 
    else pure Nothing 

desugars để

ask >>= \permissions -> 
    if hasPermission "admin" permissions 
    then map Just newUser 
    else pure Nothing 

Để hiểu điều này không, bạn sẽ phải tra cứu định nghĩa của ask, >>=pure cho ReaderT. Hãy thực hiện một đợt thay thế:

ask >>= \permissions -> ... 
{- definition of ask for ReaderT -} 
ReaderT pure >>= \permissions -> ... 
{- definition of >>= for ReaderT -} 
ReaderT \r -> 
    pure r >>= \a -> case (\permissions -> ...) a of ReaderT f -> f r 
{- function application -} 
ReaderT \r -> 
    pure r >>= \a -> 
    case (if hasPermission "admin" a 
      then map Just newUser 
      else pure Nothing) of ReaderT f -> f r 
{- definition of pure for Identity -} 
ReaderT \r -> 
    Identity r >>= \a -> 
    case (if hasPermission "admin" a 
      then map Just newUser 
      else pure Nothing) of ReaderT f -> f r 
{- definition of >>= for Identity -} 
ReaderT \r -> 
    (\a -> 
    case (if hasPermission "admin" a 
      then map Just newUser 
      else pure Nothing) of ReaderT f -> f r) r 
{- function application -} 
ReaderT \r -> 
    case (if hasPermission "admin" r 
     then map Just newUser 
     else pure Nothing) of ReaderT f -> f r 

Như bạn thấy, createUser rõ ràng chỉ là một chức năng bọc bởi ReaderT rằng đề một giá trị ("môi trường") thông qua biểu hiện của bạn. runReader bỏ qua chức năng và gọi nó với đối số được cung cấp:

runReader :: forall r a. Reader r a -> r -> a 
runReader (ReaderT f) r = f r 
Các vấn đề liên quan