2014-08-28 15 views
10

Czy istnieje sposób na usunięcie bólu z uproszczenia ekspresji?Nauka Haskell - jak uprościć wyrażenia?

Na przykład, biorąc pod uwagę to wyrażenie:

(+) <$> a <*> b $ 1 

chciałbym zobaczyć narzędzia, które byłoby wyjaśnić, co to znaczy. Jest to dość pracochłonne dla początkujących (znalezienie poprawnej instancji definicji funkcji w źródłach, sprawdzając pierwszeństwo operator) w celu uproszczenia wyrażeń z wszystkich etapów:

fmap (+) a <*> b $ 1 

Zobacz definition w Data.Functor

(.) (+) a <*> b $ 1 

Zobacz fmap w Control.Monad.Instances dla instance Functor ((->) r)

i tak dalej.

EDYCJA: Aby wyjaśnić, szukam sposobu na przepisanie wyrażenia przy użyciu rzeczywistych definicji funkcji, aby nowy użytkownik mógł zrozumieć wynik tego wyrażenia. Jak o tym powiedzieć tutaj: (<$>) = fmap? Nie wiem, jak znaleźć określoną definicję instancji (źródło) przy użyciu hoogle i innych narzędzi.

EDYCJA: Zmieniono niepoprawne oryginalne wyrażenie w celu dopasowania do następujących redukcji.

Odpowiedz

3

start ghci, :cd do katalogu podstawowego źródła czytasz, :load moduł, który Cię interesuje, i użyć polecenia :i uzyskać informację:

ghci> :i <$> 
(<$>) :: Functor f => (a -> b) -> f a -> f b 
    -- Defined in `Data.Functor' 
infixl 4 <$> 
ghci> :i $ 
($) :: (a -> b) -> a -> b -- Defined in `GHC.Base' 
infixr 0 $ 
ghci> :i . 
(.) :: (b -> c) -> (a -> b) -> a -> c -- Defined in `GHC.Base' 
infixr 9 . 

który mówi o typ, gdzie jest zdefiniowany, asocjatywność (infixl lub infixr) i pierwszeństwo (liczba, wyższa jest ciaśniejsza). Tak więc (*10) <$> a $ 1 jest odczytywany jako ((*10) <$> a) $ 1.

Po dodaniu modułu wszystkie nazwy znajdujące się w jego zasięgu będą znajdować się w zasięgu ghci. Jedynym miejscem, w którym może to być denerwujące, jest błąd w kodzie, więc nie można w nim niczego znaleźć. W takich przypadkach możesz skomentować linie, użyć undefined i prawdopodobnie również użyć wpisanych dziur, jak sugeruje behlkir (jeszcze nie grałeś z tymi zbyt dużo).

Podczas pobytu w nim, wypróbuj polecenie :? w ghci.

6

Uważam, że łatwym sposobem jest użycie typed holes dostępne w GHCi 7,8:

> (*10) <$> _a $ 1 
Found hole ‘_a’ with type: s0 -> b 
Where: ‘s0’ is an ambiguous type variable 
     ‘b’ is a rigid type variable bound by 
      the inferred type of it :: b at <interactive>:4:1 
Relevant bindings include it :: b (bound at <interactive>:4:1) 
In the second argument of ‘(<$>)’, namely ‘_a’ 
In the expression: (* 10) <$> _a 
In the expression: (* 10) <$> _a $ 1 

Więc to mówi mi, że a :: s0 -> b. Dalej jest, aby dowiedzieć się kolejność operatorów:

> :i (<$>) 
(<$>) :: Functor f => (a -> b) -> f a -> f b 
infixl 4 <$> 
> :i ($) 
($) :: (a -> b) -> a -> b 
infixr 0 $ 

Więc to mówi, że $ jest wysoce prawym asocjacyjne, a biorąc pod uwagę to typ widzimy, że jest to pierwszy argument musi być funkcją, więc a musi być funkcją (podwójne potwierdzenie). Oznacza to, że (*10) <$> a $ 1 jest taki sam jak ((*10) <$> a) $ 1, więc najpierw skupimy się na (*10) <$> a.

> :t ((*10) <$>) 
((*10) <$>) :: (Num a, Functor f) => f a -> f a 
> :t (<$> _a) 
Found hole ‘_a’ with type: f a 
Where: ‘a’ is a rigid type variable bound by 
      the inferred type of it :: (a -> b) -> f b at Top level 
     ‘f’ is a rigid type variable bound by 
      the inferred type of it :: (a -> b) -> f b at Top level 
In the second argument of ‘(<$>)’, namely ‘_a’ 
In the expression: (<$> _a) 

Więc potrzebujemy a, aby być funktorem. Jakie są dostępne wystąpienia?

> :i Functor 
class Functor (f :: * -> *) where 
    fmap :: (a -> b) -> f a -> f b 
    (<$) :: a -> f b -> f a 
     -- Defined in ‘GHC.Base’ 
instance Functor Maybe -- Defined in ‘Data.Maybe’ 
instance Functor (Either a) -- Defined in ‘Data.Either’ 
instance Functor ZipList -- Defined in ‘Control.Applicative’ 
instance Monad m => Functor (WrappedMonad m) 
    -- Defined in ‘Control.Applicative’ 
instance Control.Arrow.Arrow a => Functor (WrappedArrow a b) 
    -- Defined in ‘Control.Applicative’ 
instance Functor (Const m) -- Defined in ‘Control.Applicative’ 
instance Functor [] -- Defined in ‘GHC.Base’ 
instance Functor IO -- Defined in ‘GHC.Base’ 
instance Functor ((->) r) -- Defined in ‘GHC.Base’ 
instance Functor ((,) a) -- Defined in ‘GHC.Base’ 

Więc (->) r zdarza się być jednym, który jest niesamowite, ponieważ wiemy a musi być funkcją. Z ograniczenia Num możemy ustalić, że r musi być taki sam jak Num a => a. Oznacza to, że (*10) <$> a :: Num a => a -> a. Z tego następnie zastosujemy do niego 1, a otrzymamy (*10) <$> a $ 1 :: Num a, gdzie a jest jakąś nieznaną funkcją.

Wszystko to można znaleźć za pomocą GHCi przy użyciu :t i :i wraz z wpisanymi dziurami. Oczywiście, zaangażowanych jest sporo kroków, ale nigdy nie zawodzi, gdy próbujesz złamać złożone wyrażenie, wystarczy spojrzeć na typy różnych podwyrażeń.

5

GHCi było cudownie i poprawnie zasugerowane, a ja to też sugeruję.

Ja też chcę sugerować Hoogle, ponieważ z instant wyszukiwania włączona (w górnym pasku po prawej stronie znajduje się przycisk do niego), można wyszukać funkcji bardzo szybko, może ona dostarczyć dużo, znacznie więcej informacji niż GHCi, a najlepsze jest to, że nie musisz wymieniać modułów do wyszukiwania w nich . Jest to w przeciwieństwie do GHCi gdzie trzeba importować pierwszy:

ghci> :t pure 
<interactive>:1:1: Not in scope: ‘pure’ 
ghci> :m +Control.Applicative 
ghci> :t pure 
pure :: Applicative f => a -> f a 

Hoogle Link powyżej jest tylko jeden (ze strony Haskell.org). Hoogle to program, który możesz również zainstalować na swoim komputerze (cabal install hoogle) i wykonywać zapytania z wiersza poleceń (hoogle your-query).
Sidenote: będziesz musiał uruchomić hoogle data, aby najpierw zebrać informacje. Wymaga to wget/curl, więc jeśli jesteś w systemie Windows, prawdopodobnie będziesz musiał najpierw uzyskać ścieżkę this (lub oczywiście curl dla Windows). W Linuksie prawie zawsze jest wbudowany (jeśli nie masz go na Linuksie, tylko apt-get). Nigdy nie używam Hoogle z linii poleceń, ale nie jest to tak łatwo dostępne, ale nadal może być bardzo pomocne, ponieważ niektóre edytory tekstu i ich wtyczki mogą z nich korzystać.

Alternatywnie można użyć funkcji FPComplete's Hoogle, która jest czasami bardziej satysfakcjonująca (ponieważ z mojego doświadczenia wynika, że ​​jest ona świadoma większej liczby bibliotek zewnętrznych, używam jej tylko w tych "sesjach Hoogling").

Przy okazji jest również Hayoo!.

W Hoogle prawdopodobnie> 95% czasu nie będzie musiał to zrobić, ale +Module zaimportować moduł jeśli z jakiegoś powodu to nie jest przeszukiwana (co ma miejsce czasami bibliotek 3rd party) .
Można również odfiltrować moduły przez -Module.
Na przykład: destroyTheWorld +World.Destroyer -World.Destroyer.Mercy znalezienia destroyTheWorld i upewnić się, że nie patrzymy na miłosiernej sposób to zrobić (ten jest bardzo przydatny z modułów o takich samych nazwach funkcji dla różnych wersjach, takich jak te, które w Data.ByteString & Data.ByteString.Lazy, Data.Vector & Data.Vector.Mutable itp.).

Oh i jeszcze jedną niesamowitą zaletą Hoogle jest to, że nie tylko pokazuje podpis funkcji, ale może również przenieść się na strony modułu Haddock, dzięki czemu zyskujesz także dokumentację + na tych stronach, gdy są dostępne, możesz kliknąć na "Źródło" po prawej stronie każdej funkcji, aby sprawdzić, w jaki sposób jest realizowany w celu uzyskania jeszcze większej ilości informacji.

To wykracza poza zakres pytania, ale Hoogle służy również do sprawdzania sygnatur funkcji, które są po prostu ... niezwykle pomocne. Jeśli chcę funkcji, która pobiera numer indeksu i listę i daje mi element w tym indeksie, i zastanawiam się, czy jest już wbudowany, mogę go wyszukać w ciągu kilku sekund.
Wiem, że funkcja pobiera liczbę i listę, i podaje mi element listy, więc podpis funkcji musi wyglądać następująco: Int -> [a] -> a (lub ogólnie: Num a => a -> [b] -> b), a obie instancje pokazują, że naprawdę istnieje funkcja do tego ((!!) i genericIndex).

Gdzie GHCi ma przewagę, to że można bawić się wyrażeniami, eksplorować je itd. Wiele razy, gdy mamy do czynienia z funkcjami abstrakcyjnymi, oznacza to dużo.
Będąc w stanie :l (oad) jest bardzo pomocny, jak-dobrze.

Jeśli po prostu szukasz sygnatur funkcji, możesz połączyć zarówno Hoogle, jak i GHCi.
W GHCi możesz wpisać :! cmd, a GHCi wykona cmd w linii poleceń i wydrukuje wyniki. Oznacza to, że możesz również Hoogle w GHCi, np. :! hoogle void.