2014-10-06 13 views
7

Powiedzmy mam następujące newtype:Korzystanie Funkcje `` newtype A` na A`

newtype Foo = Foo Integer deriving (Eq, Show)

Czy istnieje zwięzły sposób, aby dodać dwa Foo Add

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

lub uzyskaj maksimum:

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

Jestem ciekawy, czy to możliwe, aby łatwo korzystać z funkcji a dla newtype a zamiast zrobić:

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

Odpowiedz

12

Podobnie jak haskell98 umie czerpać te Eq i Show instancje dla ciebie, możesz włączyć rozszerzenia GeneralizedNewtypeDeriving do GHC uzyskać instancje Num i Ord musisz:

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

Czy ta technika jest uważana za idiomatyczną? –

+3

@KevinMeredith Tak, jest to idiomatyczne. Ale dla własnego oświecenia powinieneś sam spróbować napisać instancje 'Ord' i' Num'. – augustss

2

Aby uzyskać operacji matematycznych, trzeba będzie dokonać Foo instancję Num typeclass. Oznacza to, że będziesz musiał zdefiniować (+), (*), abs, signum, fromInteger i albo negate lub (-) dla Foo. Po zdefiniowaniu tych funkcji bezpłatnie uzyskasz pozostałe funkcje działające pod Num.

Aby uzyskać max i podobne funkcje do pracy, należy wykonać Foo instancję z Ord. Wymaga to definicji compare lub (<=).

Ogólnie rzecz biorąc, można użyć :t w ghci, aby znaleźć typ funkcji, która obejmuje typografie, z którymi działa. Następnie musisz określić, jaki minimalny zestaw funkcji musisz zdefiniować dla tej typklasy.

10

Chcesz podnieść funkcje typu Integer -> Integer -> Integer do Foo -> Foo -> Foo. Aby to zrobić można definiować funkcje użytkowe:

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 

Następnie można go używać w następujący sposób:

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

liftFoo2 max (Foo 10) (Foo 5) 

Ma to tę zaletę, że nie wymaga rozszerzenia.


Inną opcją jest, aby definicja Foo newtype bardziej dopuszczalne tak, że można zrobić to wystąpienie Functor i Applicative:

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 

Teraz można wykonać następujące czynności:

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

max <$> foo 10 <*> foo 5 

Ponieważ foo specjalizuje się w typie Integer, nie tracisz korzyści z kontroli typu.

3

Można również użyć do tego celu bezpiecznych koercji. W przybliżeniu używasz Data.Coerce.coerce, aby automatycznie owijać/rozwijać nowy typ.

> 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 

Zauważ, że działa świetnie z funkcji monomorficznych jak f, ale w mniejszym stopniu z polimorficznych funkcji, takich jak succ, ponieważ w tym ostatnim przypadku wymagane jest typ adnotacji.

Powiązane problemy