2013-08-07 22 views
7

Moja intencja jest prosta. Chcę zawijać funkcje typu a -> b do String -> String (tak, że kilka heterogenicznych funkcji można umieścić na liście). Więc piszę:Przekształcanie funkcji typu `a -> b` w funkcje typu` String -> String` w Haskell

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

Jednak ghc skarg:

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 

Chcę wiedzieć, dlaczego mój kawałek kodu nie będzie działać i jaki rodzaj hacki jest potrzebne do osiągnięcia mojego celu?

Dzięki.

+3

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

+0

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

+0

@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

Odpowiedz

13

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.

10

Aby określić typ read s wyraźnie, musisz coś podobnego ScopedTypeVariables:

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

ponieważ w przeciwnym razie :: a adnotacja wewnątrz funkcji odnosi się do innego typu od a w podpisie typu (to niejawnie oznacza :: forall a. a). Należy jednak pamiętać, że można również po prostu upuścić typ adnotacji w całości:

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

Ponieważ rodzaj read s można wywnioskować. Który również pozwala uprościć organizmowi

wrap f = show . f . read 
Powiązane problemy