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.
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
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
@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