2014-10-06 10 views
7

Diciamo che ho il seguente newtype:Uso delle funzioni di `` a` su Newtype a`

newtype Foo = Foo Integer deriving (Eq, Show)

Esiste un modo conciso per aggiungere due Foo s':

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

o ottieni il massimo:

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

Sono curioso di sapere se è possibile utilizzare facilmente le funzioni di a per un newtype a piuttosto che fare:

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

risposta

12

Proprio come haskell98 sa come derivare quelli Eq e Show casi per voi, è possibile attivare l'estensione GeneralizedNewtypeDeriving per GHC per ottenere le istanze Num e Ord necessari:

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

Sta usando questa tecnica considerata idiomatica? –

+3

@KevinMeredith Sì, è idiomatico. Ma solo per la tua illuminazione, dovresti provare a scrivere le istanze di 'Ord' e' Num'. – augustss

2

Per ottenere operazioni matematiche, è necessario fare Foo un'istanza del Num typeclass. Ciò significa che sarà necessario definire (+), (*), abs, signum, fromInteger e negate o (-) per Foo. Una volta definite queste funzioni, otterrete il resto delle funzioni che funzionano su Num gratuitamente.

Per ottenere max e simili funzioni per funzionare, è necessario creare Foo un'istanza di Ord. Ciò richiede definizioni di compare o (<=).

In generale, è possibile utilizzare :t in ghci per trovare il tipo di una funzione, che include i tipi di carattere con cui funziona. Quindi devi solo determinare quale set minimo di funzioni devi definire per quel typeclass.

10

Si desidera sollevare funzioni del tipo Integer -> Integer -> Integer a Foo -> Foo -> Foo. Per farlo è possibile definire funzioni di utilità:

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 

allora si potrebbe utilizzare come segue:

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

liftFoo2 max (Foo 10) (Foo 5) 

Questo ha il vantaggio di non richiedere una proroga.


Un'altra opzione è quella di rendere la definizione della Foo newtype più consentito in modo che si potrebbe fare è un esempio di Functor e 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 

Ora si può fare come segue:

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

max <$> foo 10 <*> foo 5 

Perché foo è specializzato nel tipo Integer che non si perde benefici del controllo del tipo.

3

È inoltre possibile utilizzare per la coercizione sicura per questo. Approssimativamente si utilizza Data.Coerce.coerce per avvolgere/scartare automaticamente il newtype.

> 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 

Nota che funziona grande con funzioni monomorfiche come f, ma meno con funzioni polimorfiche come succ, dato che in quest'ultimo caso è necessario un tipo di annotazione.

Problemi correlati