2014-10-06 22 views
7

Hãy nói rằng tôi có sau newtype:Sử dụng chức năng của `a` trên 'Newtype a`

newtype Foo = Foo Integer deriving (Eq, Show)

Có cách ngắn gọn để thêm hai Foo 's:

(Foo 10) + (Foo 5) == Foo 15

hoặc nhận tối đa:

max (Foo 10) (Foo 5) == Foo 5?

tôi tò mò nếu nó có thể dễ dàng sử dụng các chức năng của a cho một newtype a chứ không phải làm:

addFoo :: Foo -> Foo -> Foo 
addFoo (Foo x) (Foo y) = Foo $ x + y 

Trả lời

12

Cũng như haskell98 biết làm thế nào để lấy được những EqShow trường hợp cho bạn, bạn có thể bật phần mở rộng GeneralizedNewtypeDeriving để GHC để có được những NumOrd trường hợp bạn cần:

Prelude> :set -XGeneralizedNewtypeDeriving 
Prelude> newtype Foo = Foo Integer deriving (Eq, Show, Num, Ord) 
Prelude> (Foo 10) + (Foo 5) == Foo 15 
True 
Prelude> max (Foo 10) (Foo 5) == Foo 5 
False 
+0

Sử dụng kỹ thuật này có được coi là thành ngữ không? –

+3

@KevinMeredith Vâng, nó là thành ngữ. Nhưng chỉ cho giác ngộ của riêng bạn, bạn nên thử viết bản thân 'Ord' và' Num'. – augustss

2

Để có được các phép toán, bạn sẽ cần phải thực hiện Foo một thể hiện của các Num typeclass. Điều này có nghĩa là bạn sẽ phải xác định (+), (*), abs, signum, fromIntegernegate hoặc (-) cho Foo. Một khi bạn đã xác định các chức năng này, bạn sẽ nhận được phần còn lại của các chức năng hoạt động trên Num miễn phí.

Để nhận max và các chức năng tương tự để hoạt động, bạn cần phải thực hiện Foo một phiên bản Ord. Điều này yêu cầu các định nghĩa của compare hoặc (<=).

Nói chung, bạn có thể sử dụng :t trong ghci để tìm loại chức năng, bao gồm kiểu chữ mà nó hoạt động. Sau đó, bạn chỉ cần xác định những hàm tối thiểu nào bạn phải định nghĩa cho typeclass đó.

10

Bạn muốn nhấc chức năng của loại Integer -> Integer -> Integer đến Foo -> Foo -> Foo. Để làm như vậy bạn có thể xác định các chức năng tiện ích:

liftFoo :: (Integer -> Integer) -> Foo -> Foo 
liftFoo f (Foo a) = Foo $ f a 

liftFoo2 :: (Integer -> Integer -> Integer) -> Foo -> Foo -> Foo 
liftFoo2 f (Foo a) (Foo b) = Foo $ f a b 

-- and so on 

Sau đó, bạn có thể sử dụng nó như sau:

liftFoo2 (+) (Foo 10) (Foo 5) 

liftFoo2 max (Foo 10) (Foo 5) 

này có lợi thế là không đòi hỏi một phần mở rộng.


Một lựa chọn khác là làm cho định nghĩa của Foo Newtype phép nhiều hơn như vậy mà bạn có thể làm cho nó một thể hiện của FunctorApplicative:

import Control.Applicative 

newtype Foo a = Foo a deriving (Eq, Show) 

foo :: Integer -> Foo Integer 
foo = Foo 

instance Functor Foo where 
    fmap f (Foo a) = Foo $ f a 

instance Applicative Foo where 
    pure = Foo 
    (Foo f) <*> (Foo a) = Foo $ f a 

Bây giờ bạn có thể làm như sau:

(+) <$> foo 10 <*> foo 5 

max <$> foo 10 <*> foo 5 

foo chuyên loại Integer bạn không mất bất kỳ lợi ích của việc kiểm tra loại.

3

Bạn cũng có thể sử dụng cưỡng chế an toàn cho việc này. Gần như bạn sử dụng Data.Coerce.coerce để tự động bao bọc/bỏ kiểu mới.

> import Data.Coerce 
> newtype Foo = Foo Integer deriving (Eq, Show, Ord) 
> coerce (Foo 1) :: Integer 
1 
> let f :: Integer -> Integer ; f x = x + 1 
> coerce f (Foo 10) 
Foo 11 
> coerce (succ :: Integer -> Integer) (Foo 10) :: Foo 
Foo 11 
> coerce (max :: Integer -> Integer -> Integer) (Foo 10) (Foo 5) :: Foo 
Foo 10 

Lưu ý rằng nó hoạt động tuyệt vời với chức năng monomorphic như f, nhưng ít như vậy với chức năng đa hình như succ, vì trong trường hợp sau một loại chú thích là bắt buộc.

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