2015-07-03 20 views
6

Penso che la firma di tipo sarà simile f :: a -> [Int] dati di input sarebbe simile data NamedPoint = NamedPoint String Int IntEsiste una funzione generica che prende una struttura dati e restituisce tutti gli interi in essa contenuti?

data Person = Name Int Int Int 

e di utilizzarlo nel REPL sarebbe simile a questa:

>> let namedPoint = NamedPoint "hey" 1 2 
>> let tommy = Person "Tommy" 1 2 3 
>> f namedPoint 
>> [1,2] 
>> f Tommy 
>> [1,2,3] 

Penso che questo sarebbe utile come un'alternativa ai record per quando sei troppo pigro per scrivere getter per i dati con un sacco di parametri.

+3

Se i dati in ingresso sta per essere '' NamedPoint '' perché la funzione non può essere digitata come '' f :: NamedPoint -> [Int] ''? – zegkljan

+0

In questo caso potrebbe. Ma stavo pensando che questo comportamento potrebbe essere utile per estrarre tipi generalizzati. modifica male la mia domanda per essere più specifico. – user514156

+0

Per questa attività, uniplate è la scelta più semplice. La tua f è semplicemente la funzione universeBi. – augustss

risposta

10

La classe Data è in grado di questo. Ho trovato che il modo più semplice per lavorare con esso è con la traversal template dal pacchetto lens. Questo essenzialmente consente di impostare o ottenere qualsiasi cosa con un'istanza Data. In ghci:

> import Data.Data 
> import Control.Lens 
> import Data.Data.Lens 

> -- Data is a derivable class 
> :set -XDeriveDataTypeable 
> data NamedPoint = NamedPoint String Int Int deriving (Data, Show) 
> data Person = Name String Int Int Int deriving (Data, Show) 
> let namedPoint = NamedPoint "hey" 1 2 
> let tommy = Name "Tommy" 1 2 3 

> let ints = toListOf template :: Data a => a -> [Int] 
> ints namedPoint 
[1,2] 
> ints tommy 
[1,2,3] 

Perché template è un attraversamento, è anche possibile mappare più di valori (ma potrebbe essere necessario specificare il tipo):

> namedPoint & template *~ (10 :: Int) 
NamedPoint "hey" 10 20 
> import Data.Char 
> tommy & template %~ toUpper 
Name "TOMMY" 1 2 3 
9

Questo non è possibile con una funzione del tipo di firma che hai descritto. Pensa a cosa significa il f :: a -> [Int]: f dovrebbe essere una funzione che prende un valore di qualsiasi tipo possibile di e restituisce un elenco di Int s. Come dovrebbe essere definita una tale funzione? L'unica definizione possibile è che ignora l'argomento e restituisce un valore costante, qualcosa di simile a

f :: a -> [Int] 
f _ = [0] 

Se si sa che cosa il vostro a sta per essere, perché non basta usare quel tipo? Come questo:

f :: NamedPoint -> [Int] 
f (NamedPoint _ a b) = [a, b] 

Se si voleva qualche funzione "generale" ritorno tutte Int s da un tipo di dati, una possibilità potrebbe essere quella di definire un typeclass

class IntContainer a where 
    f :: a -> [Int] 

e quindi definire le istanze per i tipi di dati si è interessati a

instance IntContainer NamedPoint where 
    f (NamedPoint _ a b) = [a, b] 
+0

Penso che questo funzionerà per il mio caso d'uso grazie! – user514156

+1

"questo non è possibile" è un po 'esagerato - non è banale e non, in senso stretto, possibile con la firma esatta menzionata ('a :: [Int]') ma è sicuramente possibile ottenere ciò che l'OP chiede. –

+0

@ErikAllik Grazie per l'avviso, non lo sapevo. Ho modificato la risposta per essere più accurata. – zegkljan