2010-03-07 40 views
110

Tôi không hiểu "nâng" là gì. Trước tiên tôi có nên hiểu các monads trước khi hiểu "thang máy" là gì? (Tôi hoàn toàn không biết gì về monads, quá :) Hoặc ai đó có thể giải thích cho tôi với những từ đơn giản?"Nâng" trong Haskell là gì?

+6

Có thể hữu ích, có thể không: http://www.haskell.org/haskellwiki/Lifting – kennytm

Trả lời

147

Nâng cao có nhiều mẫu thiết kế hơn là khái niệm toán học (mặc dù tôi hy vọng một người nào đó quanh đây giờ đây sẽ bác bỏ tôi bằng cách hiển thị mức độ nâng hạng hoặc danh mục).

Thông thường bạn có một số loại dữ liệu có tham số. Một cái gì đó như

data Foo a = Foo { ...stuff here ...} 

Giả sử bạn thấy rằng có rất nhiều công dụng của Foo lấy loại số (Int, Double vv) và bạn tiếp tục phải viết mã mà unwraps những con số này, thêm hoặc nhân chúng, và sau đó kết thúc tốt đẹp chúng trở lại lên. Bạn có thể rút ngắn điều này bằng cách viết mã unwrap-and-wrap một lần. Chức năng này theo truyền thống được gọi là "nâng đỡ" bởi vì nó trông như thế này:

liftFoo2 :: (a -> b -> c) -> Foo a -> Foo b -> Foo c 

Nói cách khác bạn có một chức năng mà phải mất một chức năng hai đối số (ví dụ như các nhà điều hành (+)) và biến nó thành các chức năng tương đương cho Foos.

Vì vậy, bây giờ bạn có thể viết

addFoo = liftFoo2 (+) 

Edit: thêm thông tin

Bạn có thể dĩ nhiên có liftFoo3, liftFoo4 và vân vân. Tuy nhiên điều này thường không cần thiết.

Bắt đầu với các quan sát

liftFoo1 :: (a -> b) -> Foo a -> Foo b 

Nhưng đó là chính xác giống như fmap.Vì vậy, chứ không phải là liftFoo1 bạn sẽ viết

instance Functor Foo where 
    fmap foo = ... 

Nếu bạn thực sự muốn đặn hoàn chỉnh sau đó bạn có thể nói

liftFoo1 = fmap 

Nếu bạn có thể làm Foo thành một functor, có lẽ bạn có thể làm cho nó trở thành một functor applicative. Trong thực tế, nếu bạn có thể viết liftFoo2 thì dụ applicative trông như thế này:

import Control.Applicative 

instance Applicative Foo where 
    pure x = Foo $ ... -- Wrap 'x' inside a Foo. 
    (<*>) = liftFoo2 ($) 

Các (<*>) điều hành cho Foo có loại

(<*>) :: Foo (a -> b) -> Foo a -> Foo b 

Nó áp dụng các chức năng bao bọc với giá trị gói. Vì vậy, nếu bạn có thể thực hiện liftFoo2 thì bạn có thể viết điều này về mặt nó. Hoặc bạn có thể thực hiện trực tiếp và không bận tâm với liftFoo2, bởi vì các mô-đun Control.Applicative bao gồm

liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c 

và tương tự như vậy có liftAliftA3. Nhưng bạn không thực sự sử dụng chúng rất thường xuyên vì có một nhà điều hành

(<$>) = fmap 

này cho phép bạn viết:

result = myFunction <$> arg1 <*> arg2 <*> arg3 <*> arg4 

Thuật ngữ myFunction <$> arg1 trả về một chức năng mới được bọc trong Foo. Điều này lần lượt có thể được áp dụng cho đối số tiếp theo bằng cách sử dụng (<*>), v.v. Vì vậy, bây giờ thay vì có một chức năng nâng cho tất cả các arity, bạn chỉ cần có một chuỗi daisy của các ứng dụng.

+20

Có thể đáng nhắc nhở rằng thang máy phải tôn trọng luật tiêu chuẩn 'lift id == id' và' lift (f. G) == (lift f). (nâng g) '. –

+11

Thang máy thực sự là "một loại hoặc một cái gì đó". Carlos đã chỉ liệt kê các luật Functor, trong đó 'id' và' .' là thành phần mũi tên và mũi tên của một số thể loại tương ứng. Thông thường khi nói về Haskell, danh mục được đề cập là "Hask", có mũi tên là hàm Haskell (nói cách khác, 'id' và' .' tham chiếu đến các hàm Haskell mà bạn biết và yêu thích). –

+0

tôi thích cách bạn bắt đầu bằng cách đề cập đến một loại có tham số. Đây là ý tưởng quan trọng nhất về Monad. Tôi vẫn không thể tin rằng trong cuốn sách: thế giới thực haskell tác giả đã không giải thích rằng Monad chỉ đơn giản là một lớp loại (kiểm tra các chương về monads và monad lập trình trên cuốn sách đó) – osager

10

Lifting là một khái niệm mà cho phép bạn chuyển đổi một chức năng vào một chức năng tương ứng trong vòng một (thường là tổng quát hơn) thiết

hãy nhìn vào http://haskell.org/haskellwiki/Lifting

+34

Vâng, nhưng trang đó bắt đầu "Chúng tôi thường bắt đầu bằng một hàm biến đổi (covariant) ...". Không chính xác người mới thân thiện. –

+3

Nhưng "functor" được liên kết, do đó, newbie có thể chỉ cần nhấp vào đó để xem Functor là gì. Phải thừa nhận rằng, trang được liên kết không tốt lắm. Tôi cần phải có một tài khoản và sửa lỗi đó. – jrockway

+4

Đó là vấn đề tôi đã thấy trên các trang lập trình chức năng khác; mỗi khái niệm được giải thích theo các khái niệm khác (không quen thuộc) cho đến khi newbie đi vòng tròn đầy đủ (và vòng uốn cong). Phải là một cái gì đó để làm với thích đệ quy. – DNA

19

Hãy bắt đầu với một ví dụ:

> replicate 3 'a' 
"aaa" 
> :t replicate 
replicate :: Int -> a -> [a] 
> :t liftA2 replicate 
liftA2 replicate :: (Applicative f) => f Int -> f a -> f [a] 
> (liftA2 replicate) [1,2,3] ['a','b','c'] 
["a","b","c","aa","bb","cc","aaa","bbb","ccc"] 
> :t liftA2 
liftA2 :: (Applicative f) => (a -> b -> c) -> (f a -> f b -> f c) 

liftA2 chuyển đổi chức năng của các loại đồng bằng thành chức năng của các loại này được bọc trong Applicative, chẳng hạn như danh sách, IO, v.v ...

Mức tăng phổ biến khác là lift từ Control.Monad.Trans. Nó biến đổi một hành động đơn thuần của một đơn nguyên thành một hành động của một đơn nguyên được biến đổi.

Nói chung, nâng "nâng" chức năng/hành động thành loại "được bao bọc".

Cách tốt nhất để hiểu điều này, và monads vv và để hiểu lý do tại sao chúng hữu ích, có lẽ là để mã và sử dụng nó. Nếu có bất cứ điều gì bạn mã hóa trước đây mà bạn nghi ngờ có thể hưởng lợi từ điều này (tức là điều này sẽ làm cho mã đó ngắn hơn, vv), chỉ cần thử nó ra và bạn sẽ dễ dàng nắm bắt được khái niệm.

34

Paul và yairchu là cả hai giải thích tốt.

Tôi muốn thêm rằng chức năng đang được dỡ bỏ có thể có số lượng đối số tùy ý và chúng không nhất thiết phải cùng loại. Ví dụ, bạn cũng có thể định nghĩa một liftFoo1:

liftFoo1 :: (a -> b) -> Foo a -> Foo b 

Nói chung, việc dỡ bỏ các chức năng mà phải mất 1 lập luận được thể hiện trong kiểu lớp Functor, và các hoạt động nâng được gọi fmap:

fmap :: Functor f => (a -> b) -> f a -> f b 

Lưu ý sự giống nhau với loại của liftFoo1. Trong thực tế, nếu bạn có liftFoo1, bạn có thể làm Foo một thể hiện của Functor:

instance Functor Foo where 
    fmap = liftFoo1 

Hơn nữa, tổng quát của nâng đến một số bất kỳ các đối số được gọi là applicative phong cách. Đừng bận tâm vào việc này cho đến khi bạn nắm bắt được việc nâng các hàm bằng một số đối số cố định. Nhưng khi bạn làm, Learn you a Haskell có một chương tốt về điều này. Typeclassopedia là một tài liệu hay khác mô tả FunctorÁp dụng (cũng như các loại lớp khác; cuộn xuống chương bên phải trong tài liệu đó).

Hy vọng điều này sẽ hữu ích!

0

Theo this shiny tutorial, một functor là một số container (như Maybe<a>, List<a> hoặc Tree<a> có thể lưu trữ các yếu tố của một số loại khác, a). Tôi đã sử dụng ký hiệu Generics Java, <a>, cho loại phần tử a và nghĩ về các phần tử như quả trên cây Tree<a>. Có hàm fmap, có chức năng chuyển đổi phần tử, a->b và vùng chứa functor<a>. Nó áp dụng a->b cho mọi thành phần của vùng chứa có hiệu quả chuyển đổi nó thành functor<b>. Khi chỉ đối số đầu tiên được cung cấp, a->b, fmap đợi cho số functor<a>. Tức là, việc cung cấp a->b một mình sẽ biến chức năng mức phần tử này thành hàm functor<a> -> functor<b> hoạt động trên các vùng chứa. Điều này được gọi là nâng của hàm. Bởi vì các container cũng được gọi là một functor, các Functors hơn là Monads là một điều kiện tiên quyết cho việc nâng. Monads là loại "song song" để nâng. Cả hai đều dựa vào khái niệm Functor và làm f<a> -> f<b>. Sự khác biệt là việc nâng sử dụng a->b cho chuyển đổi trong khi Monad yêu cầu người dùng xác định a -> f<b>.

+4

Tôi đã cho bạn một nhãn hiệu xuống, bởi vì "một functor là một số container" là troll-hương vị ngọn lửa mồi. Ví dụ: các hàm từ một số 'r' thành một kiểu (hãy sử dụng' c' cho giống), là hàm Functors. Họ không "chứa" bất kỳ 'c'. Trong trường hợp này, fmap là thành phần hàm, lấy hàm 'a -> b' và một hàm« r -> a', để cung cấp cho bạn hàm mới, 'r -> b'. Vẫn không có container. Ngoài ra, nếu tôi có thể, tôi sẽ đánh dấu nó xuống một lần nữa cho câu cuối cùng. – BMeph

+1

Ngoài ra, 'fmap' là một hàm, và không" chờ "bất cứ điều gì; Các "container" là một Functor là _the toàn bộ point_ của nâng. Ngoài ra, Monads là, nếu bất cứ điều gì, một ý tưởng kép để nâng: một Monad cho phép bạn sử dụng một cái gì đó đã được nâng lên một số lần tích cực, như thể nó chỉ được nâng lên một lần - điều này được gọi là _flattening_. – BMeph

+1

@BMeph 'Để chờ',' để mong đợi', 'để dự đoán' là từ đồng nghĩa. Bằng cách nói "chức năng chờ đợi" tôi có nghĩa là "chức năng dự đoán". – Val

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