2011-12-23 16 views
9

Rodzaj (>>=) jestfunkcję podobną do (>> =), ale zwraca inną monada

(>>=) :: Monad m => m a -> (a -> m b) -> m b 

chcę funkcję, która ma typ:

(Monad m, Monad n) => m a -> (a -> n b) -> n b 

Funkcja ta mogłaby być używane do łączenia ze sobą różnych monad.

I w obliczu tego problemu, gdy starałem się dostać 3000 z argumentów wiersza polecenia -p 3000:

main = getArgs >>= (\args -> (elemIndex "-p" args) >>= (\id -> warpDebug (fromIntegral.read (args !! (id+1))) Ilm)) 

To wyraźnie nie będzie skompilować ponieważ getArgs zwraca IO [String] i elemIndex zwraca Maybe Int. Funkcja powyższego typu może być użyta do eleganckiego rozwiązania tego problemu. Moje pytanie brzmi:

  • Czy ta funkcja jest już zdefiniowana? (Hoogle nie znaleziono żadnych)
  • Jeśli nie, prawdopodobnie wynika to z jakiegoś powodu. Jaki jest więc powód? Czy jest to uważane za złą praktykę? Myślę, że jest to lepszy sposób niż użycie wyrażenia case.

Odpowiedz

22

Taka funkcja nie istnieje. W rzeczywistości, jeśli weźmiesz monokręciową tożsamość, będziesz mógł zbudować funkcję m a -> a, której oczywiście nie można zdefiniować dla wszystkich monad.

Aby rozwiązać ogólny problem "komponowania" dwóch monad, możesz zajrzeć do monad transformers.

Jednak wydaje się przesadne użycie w naszym przykładzie transformatorów Monada. Możesz po prostu zdefiniować funkcję [String] -> Maybe Args (dla niestandardowego typu Args - powiedzmy w przykładzie), która przetwarza argumenty wiersza poleceń, a następnie dopasowuje wzorzec do wyniku (lub używa maybe) z monady IO.

7

Ta funkcja nie istnieje, ponieważ nie ma sensu dla wszystkich monad. Jest to zasadniczo odpowiednik funkcji rozpakowywania monady Monad m => m a -> a - jedyna różnica polega na tym, że natychmiast umieszczasz ją w innej monadzie.

Powodem, dla którego ta funkcja nie jest zdefiniowana dla wszystkich monad jest to, że nie ma ona sensu dla niektórych z nich. Na przykład, weź Maybe: jedynym sposobem rozpakowania byłoby zgłoszenie błędu, jeśli masz Nothing, a błędy w czasie wykonywania są traktowane z góry. Bardziej ekstremalnym przykładem może być IO - używanie funkcji, która może "rozpakowywać" wartości prowadzi do dziwnego i potencjalnie niedeterministycznego zachowania.

Tak więc, ogólnie rzecz biorąc, nie ma takiej funkcji. Jednak wiele specyficznych monad do posiada takie funkcje. Doskonałym przykładem jest runST; jest to właściwie bezpieczny sposób postępowania z państwem. Faktycznie, do ma takie funkcje odpowiednio dla obu typów: Maybe i IO(), ale mają one problemy opisane powyżej i należy ich unikać.

Rozwiązaniem twojego problemu jest sprawdzenie, czy istnieje taka funkcja dla dowolnych monad, z którymi masz do czynienia. Jeśli tak, sprawdź potencjalne pułapki - czy generuje błędy runtime lub powoduje dziwne zachowanie?

W twoim przypadku, jeśli jesteś absolutnie pewny, żeMaybe nigdy Nothing jest używać fromJust. Jednak nie jest to ogólnie dobra praktyka, więc powinieneś po prostu przykleić się do wzoru pasującego do wartości z Maybe.

2

Odpowiedź zależy od tego, czy musisz używać Monitory Może i IO razem, czy osobno.

Jeśli potrzebujesz ich użyć razem - odpowiedz brzmi, że musisz skomponować monady IO i Maybe przez skonstruowanie stosu transformatorów monad zawierających monotację IO i transformator Monad.

Jeśli trzeba je osobno, to prostsze rozwiązanie działa:

import System.Environment 
import Data.List 

main = getArgs >>= (\args -> return (elemIndex "-p" args 
    >>= \y -> return $ y + 900) >>= print) 

Zanotuj return. Więc masz monadę Maybe w wewnętrznych nawiasach (między elemIndex i 900), ale nie IO. Oznacza to, że nie możesz wykonywać akcji IO przed opuszczeniem monady Maybe, tak jak pokazałem z drukowaniem.

Powiązane problemy