5

To jest kontynuacja mojego poprzedniego question. Skopiowałem poniższy przykład z HaxlJak korzystać z aplikacji do współbieżności?

Załóżmy, że pobieram dane z serwera blogu, aby wyświetlić stronę blogu, która zawiera najnowsze posty, popularne posty i tematy postów.

Mam następujące dane na pobieranie API:

val getRecent : Server => Seq[Post] = ... 
val getPopular : Server => Seq[Post] = ... 
val getTopics : Server => Seq[Topic] = ... 

teraz muszę je komponować do wdrożenia nowej funkcji getPageData

val getPageData: Server => (Seq[Post], Seq[Post], Seq[Topic]) 

Haxl sugeruje użycie nowego monady Fetch aby API composable .

val getRecent : Fetch[Seq[Posts]] = ... 
val getPopular : Fetch[Seq[Posts]] = ... 
val getTopics : Fetch[Seq[Topic]] = ... 

Teraz mogę określić moje getPageData: Fetch[A] z monadycznego skład

val getPageData = for { 
    recent <- getRecent 
    popular <- getPopular 
    topics <- getTopics 
} yield (recent, popular, topics) 

ale to nie działa getRecent, getPopular i getTopics jednocześnie.

Haxl wskazują pomocą aplikacyjnych kompozycji <*> komponować „jednoczesne” działania (czyli funkcje, które mogą być uruchamiane jednocześnie). Więc moje pytania to:

  • Jak wdrożyć getPageData zakładając Fetch[A] jest Applicative?
  • Jak zaimplementować Fetch jako Applicative, ale nie pod numerem Monad?

Odpowiedz

4

Jak zaimplementować getPageData zakładając Fetch [A] jest aplikacją?

Wszystko, co musimy zrobić, to spadek jednowartościowy wiążą >>= na rzecz aplikacyjnej <*>. Więc zamiast

val getPageData = for { 
    recent <- getRecent 
    popular <- getPopular 
    topics <- getTopics 
} yield (recent, popular, topics) 

chcemy napisać coś takiego (w składni Haskell, przepraszam, nie mogę zrobić Scala poza czubek głowy):

getPageData = makeTriple <$> getRecent <*> getPopular <*> getTopics 
    where 
    makeTriple x y z = (x, y, z) 

Ale czy to ma pożądany efekt jest zależne od drugiego pytania!

Jak zaimplementować pobieranie jako wnioskodawcę, ale nie jako monadę?

Klucz rozróżnienie monadycznej i aplikacyjnej sekwencjonowania że monadycznego można zależy od wartości wewnątrz wartości monadycznej, natomiast aplikacyjnych <*> nie. Zauważ, że monadyczne wyrażenie dla powyższego getPageData wiąże nazwy przed osiągnięciem getTopics.Nazwy mogą mieć użyte do zmiany struktury wyrażenia, na przykład przez pobranie innego źródła danych w przypadku, gdy recent jest puste. Ale z wyrażeniem aplikacyjnym, wyniki z getRecent i nie są czynnikami w strukturze samego wyrażenia. Właściwość ta pozwala nam odpalać każdy termin w wyrażeniu aplikacyjnym jednocześnie, ponieważ znamy strukturę wyrażenia statycznie.

Wykorzystując powyższą obserwację i oczywiście konkretny kształt typu danych Fetch, możemy zaproponować odpowiednią definicję dla <*>. Myślę Poniżej przedstawiono ogólną koncepcję:

data Fetch a = Fetch { runFetch :: IO a } 

fetchF <*> fetchX = Fetch $ do 
    -- Fire off both IOs concurrently. 
    resultF <- async $ runFetch fetchF 
    resultX <- async $ runFetch fetchX 
    -- Wait for both results to be ready. 
    f <- wait resultF 
    x <- wait resultX 
    return $ f x 

Dla porównania, załóżmy, że staraliśmy się zrobić jednowartościowy wiążą z równoczesną ocenę:

fetchF >>= fetchK = Fetch $ do 
    resultF <- async $ runFetch fetchF 
    -- Oh no, we need resultF in order to produce the next 
    -- Fetch value! We just have to wait... 
    f <- wait resultF 
    fetchX <- async $ runFetch (fetchK f) 
    x <- wait $ runFetch fetchX 
    return $ f x 
+0

dzięki. Nie znam 'Haskella', dlatego właśnie pytam ... Czy definiujesz' Fetch [A] 'jako rekord z polem' runFetch' typu 'IO [A]'? – Michael

+0

To prawda, Fetch [A] ma rekordowy wynik run, który jest IO [A]. A więc Fetch [A] to po prostu nowa nazwa dla IO [A], co pozwala nam podać nowe definicje '>> =' i '<*>. –

+0

Jeszcze raz dziękuję. Myślę, że rozumiem. BTW 'Haxl' inaczej definiuje' Fetch', postaram się przeczytać ponownie i prawdopodobnie zadam więcej pytań tutaj. – Michael