2011-12-11 17 views
5

Supponiamo che io sono un po 'generica funzioneFornire un corpo funzione diversa per una generica funzione in base al tipo

genericFunc :: a -> b 
genericFunc x = doSomeHardWork 

Ma per un determinato tipo, c'è un modo molto più efficiente che genericFunc si poteva fare.

genericFunc :: ParticularType -> b 
genericFunc x = doSomeEasyWork 

Qual è il modo migliore per combinare questi due corpi di funzione nella stessa genericFunc, in modo tale che quando viene utilizzato su ParticularType, sarà doSomeEasyWork, ma quando viene utilizzato su altri tipi, sarà doSomeHardWork? Sto specificatamente escludendo l'opzione di utilizzare un nome diverso o moduli diversi.

Credo che questo possa essere fatto con un typeclass, ma sono più interessato alle soluzioni che usano la direttiva del linguaggio. Ho una vaga idea che questo possa essere fatto con le indicazioni del linguaggio, ma non ho idea di come. Punti bonus se confronti e confronti questi approcci e/o altri possibili approcci.

+0

vagamente ispirato a pensare a questa domanda: [Se qualcosa non è un elenco] (http://stackoverflow.com/questions/ 8463777/if-something-is-not-a-list-in-haskell) –

risposta

8

Questo può essere fatto con classi di tipi definendo il metodo generale nella definizione di classe e sovrascrivendolo in un'istanza. La funzione sostituita verrà sempre utilizzata.

class ContainsInt c where 
    toList :: c -> [Int] 

    -- generic function 
    elem :: Int -> c -> Bool 
    elem n x = Prelude.elem n (toList x) 

instance ContainsInt() where 
    toList _ = [] 

    -- Override the generic function for type() 
    elem _ _ = False 

Un'alternativa supportata da GHC consiste nell'utilizzare una regola di riscrittura. La regola di riscrittura dice a GHC di sostituire un'espressione con un'altra quando possibile. Se la sostituzione è mal digitata, non verrà eseguita, quindi è possibile utilizzarla per sostituire una funzione con una versione specializzata. La regola di riscrittura è data da un pragma {-# RULES #-}.

class ContainsInt c where 
    toList :: c -> [Int] 

elem :: ContainsInt c => Int -> c -> Bool 
elem n x = Prelude.elem n (toList x) 

-- Replace 'elem' by 'elemUnit' if it has the same type 
{-# RULES "elem()" forall. elem = elemUnit #-} 

elemUnit :: Int ->() -> Bool 
elemUnit _ _ = False 

regole di riscrittura vengono eseguiti a discrezione del compilatore, in modo che la funzione specializzata possono o non possono essere chiamati in una determinata situazione. Ad esempio, la riscrittura può dipendere dal fatto che il compilatore decide di inline una funzione:

foo :: ContainsInt c -> Int -> [c] -> [Bool] 
-- Must use the generic function 
foo n cs = map (elem n) cs 

useFoo :: Int -> [()] -> [Bool] 
-- If 'foo' is inlined and 'elem' is not inlined, then this function will contain a rewritable call to 'elem'. 
-- Otherwise rewriting cannot happen. 
useFoo n cs = foo n cs 
2

Con GHC, è possibile utilizzare un RULES pragma.

{-# RULES "genericFunc/easy" genericFunc = doSomeEasyWork #-} 

Questo applicherà la regola di riscrittura ogni volta che i tipi corrispondono e utilizzano altrimenti l'implementazione generica.

+0

Ogni volta che _GHC può dimostrare che i tipi corrispondono. –

+1

Ci sono dei buoni esempi di questo "in codice reale" che posso guardare? –

+1

Non utilizzare le regole, utilizzare una classe di caratteri. Questo è il motivo per cui sono destinati i typeclass. – augustss

Problemi correlati