2015-08-19 13 views
8

Ultimamente ho giocato con Haskell, e in particolare con l'intero concetto di functors. Più mi ci immergo, più mi sono concesso gli a-ha, e certamente stuzzica un po 'i miei recettori della dopamina.Inferenza di tipo Haskell per i Funcitors

Il problema con cui sono bloccato è il seguente. Ecco il codice che funziona, solleva la funzione e quindi la applica prima al valore di I/O, quindi a una lista.

replicator1 = 
    fmap (replicate 3) 

replicator2 = 
    fmap (replicate 3) 

main = do 
    replicated <- replicator1 getLine 
    print (replicator2 replicated) 

E 'molto forte la tentazione di scrivere in modo più conciso, vale a dire:

replicator = 
    fmap (replicate 3) 

main = do 
    replicated <- replicator getLine 
    print (replicator replicated) 

Una parte di me dice che è concettualmente giusto, dal momento che replicator dovrebbe essere applyable sia per IO e per elencare le istanze, ma essendo un linguaggio fortemente tipizzato, Haskell non mi permette di farlo. Penso di capire praticamente perché sta succedendo questo.

La domanda è: c'è un modo per avvicinarmi alla seconda variante? O è bello vivere con il primo?

Grazie!

+2

A lw a y s s u r il p r e d ì s u r u z io n e a t t t t a d e l t o p o l e l. – leftaroundabout

risposta

19

Il tuo codice è in realtà bene, tranne è stato eseguito nella dreaded monomorphism restriction che ha mantenuto Haskell da inferire il tipo più generale possibile per replicator.

In sostanza, con la limitazione, Haskell non sarà inferire tipi polimorfici per associazioni che non sembrano funzioni. Ciò significa che ha per selezionare un functor concreto come [] o IO per replicate e provoca un errore se si tenta di utilizzarlo in due contesti diversi.

È possibile rendere il vostro lavoro di codice in tre modi:

  • Spegnere la restrizione monomorfismo: aggiungere {-# LANGUAGE NoMonomorphismRestriction #-} nella parte superiore del modulo.

  • fanno replicatoraspetto come una funzione:

    replicator x = fmap (replicate 3) x 
    
  • Aggiungi tipo firme esplicite al codice

    replicator :: Functor f => f a -> f [a] 
    replicator = fmap (replicate 3) 
    

La terza opzione è la più idiomatica. Lo stile Good Haskell prevede l'aggiunta di firme di tipo esplicito a tutti i tuoi identificatori di primo livello. Tuttavia, è utile conoscere le altre due opzioni per capire cosa sta succedendo e per poter scrivere script usa e getta rapidi in Haskell senza preoccuparsi delle firme dei tipi.

+2

Uh, sento ancora un po 'di fluire della dopamina. Questa è una risposta spettacolare! Grazie! – SkyWriter

+0

+1 per "... è utile conoscere le altre due opzioni ... per scrivere script Haskell a prova di sporcizia rapida ..." ... un mondo di possibilità da esplorare .... – dsign

+1

@dsign non posso davvero essere d'accordo. Per esplorare le possibilità_, è molto più efficiente lasciare che le firme dei tipi ti guidino.Omettere le firme dei tipi è OK per compiti molto semplici in cui si sa già esattamente di che tipo si tratta, e non si può prendere la briga di dichiarare l'ovvio scrivendolo. OTOH, quando entri in un nuovo terreno, è lì che ti servono di più le firme per accertarti che stai andando nella giusta direzione. – leftaroundabout