2012-08-26 31 views
17

Trong những ý kiến ​​của câu hỏi Tacit function composition in Haskell, người ta đề cập làm cho một trường hợp Num cho a -> r, vì vậy tôi nghĩ rằng tôi muốn chơi với việc sử dụng ký hiệu chức năng để đại diện cho nhân:số như các chức năng nhân giống (lạ nhưng là giải trí)

{-# LANGUAGE TypeFamilies #-} 
import Control.Applicative 

instance Show (a->r) where -- not needed in recent GHC versions 
    show f = " a function " 

instance Eq (a->r) where  -- not needed in recent GHC versions 
    f == g = error "sorry, Haskell, I lied, I can't really compare functions for equality" 

instance (Num r,a~r) => Num (a -> r) where 
    (+) = liftA2 (+) 
    (-) = liftA2 (-) 
    (*) = liftA2 (*) 
    abs = liftA abs 
    negate = liftA negate 
    signum = liftA signum 
    fromInteger a = (fromInteger a *) 

Lưu ý rằng định nghĩa fromInteger có nghĩa là tôi có thể viết 3 4 có giá trị là 12, và 7 (2+8) là 70, giống như bạn mong đợi.

Sau đó, tất cả đều tuyệt vời, giải trí kỳ lạ! Xin giải thích wierdness này nếu bạn có thể:

*Main> 1 2 3 
18 
*Main> 1 2 4 
32 
*Main> 1 2 5 
50 
*Main> 2 2 3 
36 
*Main> 2 2 4 
64 
*Main> 2 2 5 
100 
*Main> (2 3) (5 2) 
600 

[Edit:. Sử dụng applicative thay vì Monad vì applicative là rất tốt nói chung, nhưng nó không làm cho nhiều sự khác biệt ở tất cả các code]

+2

Trong GHC 7.4, có thể xóa các trường hợp 'Hiển thị' và' Eq' giả, vì 'Num' không còn yêu cầu chúng nữa. – sdcvvc

+3

'Monad' quá tải ở đây. Các 'Applicative' đơn giản hơn và tổng quát hơn là đủ. – Conal

+0

@sdcvvc Tôi sẽ sớm nâng cấp, vâng. – AndrewC

Trả lời

21

Trong một biểu hiện như 2 3 4 với các phiên bản của bạn, cả hai chức năng 23 là các hàm. Vì vậy, 2 thực sự là (2 *) và có loại Num a => a -> a. 3 là giống nhau. 2 3 sau đó là (2 *) (3 *) tương tự như 2 * (3 *). Theo trường hợp của bạn, đây là liftM2 (*) 2 (3 *), sau đó là liftM2 (*) (2 *) (3 *). Bây giờ biểu thức này hoạt động mà không có bất kỳ trường hợp nào của bạn.

Vậy điều này có nghĩa là gì? Vâng, liftM2 cho các chức năng là một loại thành phần kép. Cụ thể, liftM2 f g h cũng giống như \ x -> f (g x) (h x). Vì vậy, liftM2 (*) (2 *) (3 *) sau đó là \ x -> (*) ((2 *) x) ((3 *) x). Đơn giản hóa một chút, chúng tôi nhận được: \ x -> (2 * x) * (3 * x). Vì vậy, bây giờ chúng ta biết rằng 2 3 4 thực sự là (2 * 4) * (3 * 4).

Bây giờ, tại sao liftM2 cho các chức năng hoạt động theo cách này? Chúng ta hãy nhìn vào ví dụ đơn nguyên cho (->) r (ghi nhớ rằng (->) r(r ->) nhưng chúng tôi không thể viết phần khai thác loại cấp):

instance Monad ((->) r) where 
    return x = \_ -> x 
    h >>= f = \w -> f (h w) w 

Vì vậy returnconst. >>= hơi lạ một chút. Tôi nghĩ rằng việc xem điều này dễ dàng hơn theo điều khoản của join. Đối với chức năng, join làm việc như thế này:

join f = \ x -> f x x 

Đó là, phải mất một chức năng của hai đối số và biến nó thành một chức năng của một đối số bằng cách sử dụng lập luận rằng hai lần. Đủ đơn giản. Định nghĩa này cũng có ý nghĩa. Đối với các hàm, join phải chuyển một hàm của hai đối số thành một hàm của một đối số; cách hợp lý duy nhất để làm điều này là sử dụng một đối số đó hai lần.

>>=fmap theo sau là join. Đối với các chức năng, fmap chỉ là (.).Vì vậy bây giờ >>= bằng:

h >>= f = join (f . h) 

mà chỉ là:

h >>= f = \ x -> (f . h) x x 

bây giờ chúng tôi chỉ thoát khỏi . để có được:

h >>= f = \ x -> f (h x) x 

Vì vậy, bây giờ mà chúng tôi biết làm thế nào >>= công trình , chúng ta có thể xem liftM2. liftM2 được định nghĩa như sau:

liftM2 f a b = a >>= \ a' -> b >>= \ b' -> return (f a' b') 

Chúng ta chỉ đơn giản là từng chút một. Đầu tiên, return (f a' b') biến thành \ _ -> f a' b'. Kết hợp với số \ b' ->, chúng tôi nhận được: \ b' _ -> f a' b'. Sau đó b >>= \ b' _ -> f a' b' biến thành:

\ x -> (\ b' _ -> f a' b') (b x) x 

kể từ thứ hai x bị bỏ qua, chúng tôi nhận được: \ x -> (\ b' -> f a' b') (b x) mà sau đó được giảm xuống còn \ x -> f a' (b x). Vì vậy, đây lá chúng tôi với:

a >>= \ a' -> \ x -> f a' (b x) 

Một lần nữa, chúng tôi thay >>=:

\ y -> (\ a' x -> f a' (b x)) (a y) y 

điều này làm giảm tới:

\ y -> f (a y) (b y) 

đó là chính xác những gì chúng ta sử dụng như liftM2 trước đó!

Hy vọng rằng hiện tại, hành vi của 2 3 4 hoàn toàn có ý nghĩa.

+0

À vâng - ngay sau khi bạn nhận được 'liftM2 (*) (2 *) (3 *) 4' Tôi thấy lý do tại sao nó được bình phương đối số cuối cùng - điều này chỉ có nghĩa là' (+) $ (2 *) 4 $ (3 *) 4'. Và '(2 3) (5 2)' có các dấu ngoặc không cần thiết, vì vậy nó chỉ là '2 4 (5 2)' và là 300 cho cùng một lý do. – AndrewC

+0

Nhân tiện, tôi hài lòng với các trường hợp Ứng dụng và Monad cho '(->) r' và nên nói như vậy trong câu hỏi, nó chỉ là bộ não của tôi vừa mới bắt đầu rò rỉ ra khỏi tai của tôi khi tôi đã làm' 2 3 4 ', và tôi thậm chí không cố gắng đánh giá bằng tay. doh! Lời giải thích của bạn sẽ làm cho nó rõ ràng hơn cho người khác mặc dù, vì vậy cảm ơn cũng có. – AndrewC

+2

@AndrewC: Tôi đã viết ra quá trình suy nghĩ của mình từ khi tôi đã tìm ra chính xác những gì '2 3 4' đã làm, vì vậy nó đã giúp bản thân mình nhiều như bất kỳ ai khác: P. –

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