Twój kod nie zadziała, ponieważ Haskell nie ponownego użytku lub typ zakres zmiennych; a
jest zupełnie innym a
(z których oba są powszechnie kwantyfikowane). To jest źródło komunikatu o błędzie: a1
; GHC jest alfa-przekształcenie program
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a1)
Jednakże typ argumentu f
jest zamocowana jest wewnątrz wrap
tak proste usunięcie typu adnotacji działa prawidłowo. Czynność staje
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s)
-- Or wrap f = show . f . read
I można go używać:
ghci> map ($ "42") [wrap (+ (7 :: Integer)), wrap (* (2.0 :: Double))]
["49","84.0"]
Zauważ, że oznacza to, że read s
ma typ nie można napisać w dół. W Haskell 2010 (lub 98) jedynym sposobem obejścia tego problemu jest użycie funkcji takiej jak asTypeOf :: a -> a -> a
; asTypeOf
to tylko const
, ale dzięki sygnaturze typu, ogranicza pierwszy, zwrócony, argument tego samego typu co drugi. Następnie, oczywiście, musisz wyczarować zmienną typu a
. Dodaje, że będzie pracować dla:
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
W GHC, aby tego uniknąć, można włączyć the ScopedTypeVariables
extension; z tym, jeśli jawnie zakwalifikujesz wszystkie zmienne typu z forall
, będą one miały zasięg podobny do nazw o wartości. Wtedy twój kod stanie się
{-# LANGUAGE ScopedTypeVariables #-}
wrap :: forall a b. (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a)
Ale pamiętaj, że nie potrzebujesz adnotacji typu dla tego prostego przykładu.
Umieszczenie wielu niejednorodnych funkcji na liście nie jest dobrym haskellem, a zawijanie ich wszystkich w transformację łańcuchów jest zdecydowanie złe. Co próbujesz zrobić w większym znaczeniu? – NovaDenizen
To jest błąd. Nie rób tego. Odrzucasz potężny system typu Haskella i wszystkie świetne kompilacje, które pasują do niego. Mechanik, który powie ci, że twój samochód jest w porządku, a potem zepsuł się na autostradzie, nie jest tak przydatny, jak mechanik, który kazał ci wydać 50 $ na naprawienie czegoś (teraz, kiedy jest w garażu), co spowoduje, że złamiesz (później, po tym, jak spowodował więcej obrażeń, jest opłata za wezwanie, a facet, który to naprawił, nie ma części, której potrzebuje z nim). Pisanie statyczne jest dobrą mechaniką, dynamiczne pisanie to ta, która mówi, że jest w porządku. – AndrewC
@NovaDenizen Zdecydowanie używam statycznego pisania. Ostatnio pisałem prosty serwer. 'a -> IO b' oznacza zaimplementowaną usługę. Jednym ze składników jest zamiana '[(String, a -> IO b)] na' Map String (a -> IO b) '. Ale system typów na to nie pozwala. W moim umyśle mam bardziej skomplikowane projektowanie typów wymuszających bezpieczeństwo typu (tak, że klient musi podawać dane wejściowe typu "a" do usługi typu "a -> IO b", w przeciwnym razie cały program nie będzie wpisywać czek). Ale haczyk jest taki, że serwer może nie obsługiwać klientów napisanych w Haskell. Tak więc mój mechanizm nie działa w takim przypadku. – tfboy