2014-10-06 19 views
7

比方說,我有以下newtype上`NEWTYPE使用了``的功能了`

newtype Foo = Foo Integer deriving (Eq, Show)

是否有添加兩個Foo的一個簡潔的方式:

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

或獲取最大值:

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

我很好奇,如果有可能很容易地使用a功能爲newtype a,而不是做:

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

回答

12

正如haskell98知道如何獲得這些EqShow情況下你,你可以打開GeneralizedNewtypeDeriving擴展GHC得到你所需要的NumOrd實例:

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

正在使用這種技術認爲慣用? – 2014-10-06 10:27:01

+3

@KevinMeredith是的,這是慣用的。但僅僅爲了你自己的啓發,你應該嘗試自己編寫'Ord'和'Num'實例。 – augustss 2014-10-06 10:46:07

2

要獲得數學運算,你將需要FooNum類型類的一個實例。這意味着您將必須定義(+),(*),abs,signum,fromIntegernegate(-)Foo。一旦你定義了這些功能,你將可以免費獲得在Num上工作的其餘功能。

要獲得max和類似功能的工作,您需要使FooOrd的實例。這需要定義compare(<=)

通常,您可以在ghci中使用:t來查找函數的類型,該函數包含它使用的類型類。然後你只需要確定你需要爲這個類型類定義哪些最小的函數集。

10

你想解除功能類型Integer -> Integer -> IntegerFoo -> Foo -> Foo。要做到這一點,你可以定義效用函數:

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 

然後如下,你可以使用它:

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

liftFoo2 max (Foo 10) (Foo 5) 

這是無需使用擴展的優勢。


另一種選擇是使Foo NEWTYPE的定義更容許這樣你可以把它的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 

現在你可以做到以下幾點:

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

max <$> foo 10 <*> foo 5 

由於foo專門針對Integer類型,因此您不會丟失任何類型檢查的好處。

3

您也可以使用安全強制爲此。大致上,您使用Data.Coerce.coerce來自動換行/拆封新類型。

> 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 

需要注意的是它作爲f的偉大工程與單態功能,但沒有那麼多態功能,如succ,因爲在後一種情況下,需要的類型註釋。