2017-10-08 21 views
13

Haskell có Functor, ApplicativeMonad trường hợp được xác định cho các chức năng (cụ thể là loại được áp dụng một phần (->) a) trong thư viện chuẩn.Trường hợp sử dụng cho các trường hợp functor/applicative/monad cho các chức năng

Hiểu những trường hợp này là một bài tập tốt về tinh thần, nhưng câu hỏi của tôi ở đây là về cách sử dụng thực tế của những trường hợp này. Tôi rất vui khi được nghe về các tình huống thực tế mà mọi người sử dụng chúng cho một số mã thực tế.

+7

[Trình đơn đọc] (http://hackage.haskell.org/package/mtl/docs/Control-Monad-Reader.html) về cơ bản chỉ là trình bao bọc newtype xung quanh '(->)'. – melpomene

+0

Tôi sử dụng chúng mọi lúc. Có lẽ bạn tự làm điều đó mà không biết: '.' chỉ là' fmap'. – Bergi

+0

@Bergi: chắc chắn, tôi đoán câu hỏi là tại sao nên sử dụng chúng thay vì chỉ sử dụng (.) –

Trả lời

3

Đôi khi bạn muốn xử lý các chức năng của biểu mẫu a -> m b (trong đó mApplicative) là Applicative s. Điều này thường xảy ra khi viết trình xác thực hoặc trình phân tích cú pháp.

Một cách để làm điều này là sử dụng Data.Functor.Compose, mà piggybacks trên Applicative trường hợp của (->) am để đưa ra một ví dụ Applicative cho các thành phần:

import Control.Applicative 
import Data.Functor.Compose 

type Star m a b = Compose ((->) a) m b 

readPrompt :: Star IO String Int 
readPrompt = Compose $ \prompt -> do 
    putStrLn $ prompt ++ ":" 
    readLn 

main :: IO() 
main = do 
    r <- getCompose (liftA2 (,) readPrompt readPrompt) "write number" 
    print r 

Có nhiều cách khác, như tạo Newtype của riêng bạn hoặc sử dụng ready-madenewtypes từ cơ sở hoặc các thư viện khác.

7

Một mẫu chung có liên quan đến Functor và các trường hợp ứng dụng của hàm là ví dụ (+) <$> (*2) <*> (subtract 1). Điều này đặc biệt hữu ích khi bạn phải nạp một chuỗi hàm với một giá trị duy nhất. Trong trường hợp này, giá trị trên tương đương với \x -> (x * 2) + (x - 1). Trong khi điều này rất gần với LiftA2, bạn có thể mở rộng mẫu này vô thời hạn. Nếu bạn có hàm f để lấy 5 tham số như a -> a -> a -> a -> a -> b bạn có thể làm như f <$> (+2) <*> (*2) <*> (+1) <*> (subtract 3) <*> (/2) và nạp nó với một giá trị duy nhất. Cũng giống như trong trường hợp dưới đây;

Prelude> (,,,,) <$> (+2) <*> (*2) <*> (+1) <*> (subtract 3) <*> (/2) $ 10 
(12.0,20.0,11.0,7.0,5.0) 

Edit: tín dụng cho một tái comment của @ Will Ness cho một lời nhận xét của tôi dưới một chủ đề khác, ở đây có một cách sử dụng tuyệt đẹp của applicative qua chức năng;

Prelude> let isAscending = and . (zipWith (<=) <*> drop 1) 
Prelude> isAscending [1,2,3,4] 
True 
Prelude> isAscending [1,2,5,4] 
False 
+0

Cảm ơn bạn đã nhập. Vì vậy, ví dụ 5-tuple bạn đã đăng. Nó có thể được viết lại thành '(\ x -> (x + 2, x * 2, x + 1, x-3, x/2)) 10' - lợi thế của ứng dụng ở đây là không cần lặp lại 'x'? –

+0

@Eli Bendersky Sự khác biệt chính là, trong dạng ứng dụng nếu bạn biết có bao nhiêu tham số chức năng chính, toàn bộ điều có chức năng phân tách (bạn có thể tự động soạn tham số theo cách bạn thích) trong khi ở dạng lambda bạn phải có một hàm lambda rắn ở bàn tay. – Redu

+0

bạn đang nói rằng chúng tôi đã có một chức năng, như '(,,,,)' và không phải viết lambda bằng tay? –

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