2013-08-07 7 views
7

La mia intenzione è semplice. Voglio racchiudere le funzioni di tipo a -> b in String -> String (in modo che un gruppo di funzioni eterogenee possa essere inserito in una lista). Così scrivo:Trasformare le funzioni di tipo `a -> b` in quelle di tipo` String -> String` in Haskell

wrap :: (Read a, Show b) => (a -> b) -> (String -> String) 
wrap f = \s -> show $ f (read s :: a) 

Tuttavia, ghc lamentele:

Could not deduce (Read a1) arising from a use of `read' 
from the context (Read a, Show b) 
    bound by the type signature for 
      wrap :: (Read a, Show b) => (a -> b) -> String -> String 

Voglio sapere il motivo per cui il mio pezzo di codice non funzionerà e che tipo di hack è necessario per raggiungere il mio obiettivo?

Grazie.

+3

Inserire un gruppo di funzioni eterogenee in una lista non è un buon haskell, e avvolgerli tutti nella trasformazione String è sicuramente un haskell scadente. Cosa stai cercando di fare in un senso più ampio? – NovaDenizen

+0

Questo è un errore. Non farlo. Stai buttando via il potente sistema di tipi Haskell e tutto il grande controllo in fase di compilazione che ne consegue. Un meccanico che ti dice che la tua macchina va bene e poi si rompe in autostrada non è tanto utile quanto un meccanico che ti dice di spendere $ 50 per aggiustare qualcosa (ora, mentre è nel garage) che ti farà rompere verso il basso (più tardi, dopo aver causato più danni, c'è una tassa di chiamata e il ragazzo che la fissa non ha la parte di cui ha bisogno con lui). La tipizzazione statica è la buona meccanica, la digitazione dinamica è quella che dice che va bene. – AndrewC

+0

@NovaDenizen Sono decisamente interessato alla digitazione statica. Recentemente ho scritto un semplice server. 'a -> IO b' indica un servizio implementato. Un componente consiste nel convertire '[(String, a -> IO b)]' in 'Map String (a -> IO b)'. Ma il sistema di tipi non lo consente. Nella mia mente ho un design più complesso di tipi per far rispettare la sicurezza del tipo (in modo che il client debba inserire un input di tipo 'a' al servizio di tipo' a -> IO b 'altrimenti l'intero programma non scriverà dai un'occhiata). Ma il problema è che il server potrebbe non servire affatto i client scritti in Haskell. Quindi il mio meccanismo non funziona in quel caso. – tfboy

risposta

13

Il codice non funzionerà perché Haskell non ri-uso o tipo portata variabili; il a in wrap :: (Read a, Show b) => (a -> b) -> (String -> String) è completamente diverso da read s :: a (e sono entrambi quantificati universalmente). Questa è la fonte dello a1 nel messaggio di errore; GHC è alfa-conversione del programma in

wrap :: (Read a, Show b) => (a -> b) -> (String -> String) 
wrap f = \s -> show $ f (read s :: a1) 

Tuttavia, tipo di argomento f s' è fissato all'interno wrap, così semplicemente rimuovendo il tipo di annotazione funziona bene. La funzione diventa

wrap :: (Read a, Show b) => (a -> b) -> (String -> String) 
wrap f = \s -> show $ f (read s) 
    -- Or wrap f = show . f . read 

E lo si può utilizzare:

ghci> map ($ "42") [wrap (+ (7 :: Integer)), wrap (* (2.0 :: Double))] 
["49","84.0"] 

Si noti che questo significa che read s ha un tipo non è possibile scrivere. In Haskell 2010 (o 98), l'unico modo per aggirare questo è usare una funzione come asTypeOf :: a -> a -> a; asTypeOf è solo const, ma grazie alla sua firma del tipo, vincola il suo argomento first, ritornato, dello stesso tipo del suo secondo. Quindi, ovviamente, dovresti evocare una variabile di tipo a. Di seguito funzionerebbe per questo:

wrap :: (Read a, Show b) => (a -> b) -> (String -> String) 
wrap f = \s -> show $ f (read s `asTypeOf` fInput) 
    where fInput = undefined 
     fOutput = f fInput -- I still can't give this a type signature 

In GHC, per evitare questo, è possibile attivare the ScopedTypeVariables extension; con quello attivo, se si qualificano esplicitamente tutte le variabili di tipo con un forall, verranno esaminate come i nomi a livello di valore. Allora il vostro codice sarebbe diventato

{-# LANGUAGE ScopedTypeVariables #-} 
wrap :: forall a b. (Read a, Show b) => (a -> b) -> (String -> String) 
wrap f = \s -> show $ f (read s :: a) 

Ma ricordate, non è necessario alcun tipo annotazioni a tutti per questo semplice esempio.

10

Per specificare il tipo di read s esplicitamente, avrete bisogno di qualcosa di simile a ScopedTypeVariables:

{-# LANGUAGE ScopedTypeVariables #-} 
... 
wrap :: forall a b. (Read a, Show b) => (a -> b) -> (String -> String) 
wrap f = \s -> show $ f (read s :: a) 

altrimenti il ​​:: a annotazioni all'interno della funzione si riferisce ad un tipo diverso dal a nella firma tipo (implicitamente significa :: forall a. a). Ma nota che si può anche semplicemente cadere il tipo di annotazione del tutto:

wrap :: (Read a, Show b) => (a -> b) -> (String -> String) 
wrap f = \s -> show $ f (read s) 

Dal momento che il tipo di read s può dedurre. Ciò consente inoltre di semplificare l'organismo a

wrap f = show . f . read 
Problemi correlati