2016-01-12 2 views
6

ho definito un tipo di dati personalizzato che contiene un singolo campo:Come posso gestire tipi di dati in stile "typedef" con un numero minimo di caratteri?

import Data.Set (Set) 

data GraphEdge a = GraphEdge (Set a) 

Definire il mio tipo di sente più semanticamente corretto, ma porta ad un sacco di boilerplate in mie funzioni. Ogni volta che voglio utilizzare il built-in Set funzioni devo scartare il set interno e successivamente Rewrap:

import Data.Set (map) 

modifyItemsSomehow :: Ord a => GraphEdge a -> GraphEdge a 
modifyItemsSomehow (GraphEdge items) = GraphEdge $ Set.map someFunction items 

Questo potrebbe essere migliorata un po ', rendendo un disco, come

import Data.Set (Set, map) 

data GraphEdge a = GraphEdge { unGraphEdge :: Set a } 

modifyItemsSomehow = GraphEdge . map someFunction . unGraphEdge 

ma questo sembra ancora lontano dall'ideale. Qual è il modo più idiomatico per gestire questo tipo di piastra quando si ha a che fare con un tipo di dati definito dall'utente che consiste in un singolo campo?

+7

Qualsiasi motivo si sta utilizzando 'dati' invece di' newtype'? – Carl

+0

@Carl Nope, solo ignoranza :-) – bdesham

risposta

5

Prima di tutto, è necessario assicurarsi di utilizzare newtype per i tipi di costruttore singolo a campo singolo. data introduce il sovraccarico di runtime e la pigrizia extra e ci impedisce di utilizzare le prime due delle seguenti tecniche.

In primo luogo, è possibile utilizzare GeneralizedNewtypeDeriving quando possibile:

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 

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

foo :: Foo Int 
foo = 0 

bar :: Foo Int 
bar = foo * 120 

In secondo luogo, è possibile utilizzare coerce per la conversione in genere tra confezioni newtype:

import Data.Coerce 

newtype Foo a = Foo a 
newtype Bar a = Bar a 

a :: [(Foo (Bar Int), Foo())] 
a = [(Foo (Bar 0), Foo())] 

b :: [(Int,())] 
b = coerce a 

In terzo luogo, è possibile utilizzare iso -s da lens per spostare in modo conciso le operazioni su/sotto costruttori newtype.

{-# LANGUAGE TemplateHaskell #-} 

import Data.Set (Set) 
import qualified Data.Set as Set 
import Control.Lens 

newtype GraphEdge a = GraphEdge (Set a) 
makePrisms ''GraphEdge 

modifyItemsSomehow :: Ord a => GraphEdge a -> GraphEdge a 
modifyItemsSomehow = _GraphEdge %~ Set.map someFunction 
+0

'lens' ora offre (solo per 7.10+, non so perché non 7.8 - potrebbe essere stato un mio errore)' coerced :: forall s t a b. (Coercible s a, Coercible t b) => Iso s t a b'. – dfeuer

+0

'lens' offre anche il modulo' Control.Lens.Wrapped' per varie forme di esecuzione di un'operazione sotto un newtype. – Carl