Труба с динамическим типом запроса/ответа?

Это кажется разумным желанием, но у меня проблемы с типом. Я хотел бы иметь Client, который может отправить список параметров Server, который выберет один и вернет выбранный элемент. Что-то вроде этого:

module Toy where

import Pipes

asker :: Monad m => () -> Client ([a], a -> String) a m ()
asker () = do
    _ <- request ([0.0, 2.0], show)
    _ <- request (["3", "4"], show)
    return ()

Идея состоит в том, что сервер может вызвать функцию a -> String для каждого элемента списка, чтобы отобразить их пользователю. Я хотел бы иметь возможность изменять a, если список и функция совпадают.

Возможно ли что-то подобное? Может быть, нужные мне ограничения можно каким-то образом закодировать в GADT?


person ajp    schedule 06.07.2013    source источник
comment
Что сервер делает с запросом? Что он может сделать, если он даже не знает, какой тип он получит? Если он может только преобразовать этот тип в строку (используя функцию), почему бы вам не передать строку в первом случае?   -  person bennofs    schedule 07.07.2013
comment
Думаю, это правда. Было бы неплохо не преобразовывать ответ обратно из строки, но это не так уж и сложно.   -  person ajp    schedule 07.07.2013
comment
@ajp: идея заключается в том, что (а) клиент отправляет значения некоторого типа, который соответствует интерфейсу (например, Show); (b) сервер принимает значения любого такого типа; и (c) когда клиент получает ответ от сервера, он знает, какого типа он был отправлен? Шаг (c) будет камнем преткновения (подумайте, что произойдет, если сервер решит ответить одним и тем же значением дважды или не ответит вообще); вам, вероятно, понадобится тип суммы или что-то вроде Typeable/Dynamic. Экзистенциалы (то, что вы получаете с помощью GADT) никогда нельзя распаковать, чтобы узнать, каким был исходный тип.   -  person Antal Spector-Zabusky    schedule 07.07.2013
comment
да, это почти все. Спасибо!   -  person ajp    schedule 07.07.2013


Ответы (1)


Вы не можете сделать это так, как вы просили, но вы можете немного схитрить и получить что-то почти такое же хорошее:

{-# LANGUAGE ExistentialQuantification #-}

module Toy where

import Control.Monad
import Pipes
import Pipes.Prelude (foreverK)

data Request = forall a . Request [a] (a -> String)

asker :: Monad m => () -> Client Request Int m ()
asker () = do
    _ <- request (Request [0.0, 2.0] show)
    _ <- request (Request ["3", "4"] show)
    return ()

server :: Request -> Server Request Int IO r
server = foreverK $ \req -> case req of
    Request as f -> do
        choice <- lift $ do
            let select = do
                putStrLn "Select an option"
                forM_ (zip [0..] as) $ \(n, a) ->
                    putStrLn $ show n ++ ": " ++ f a
                n <- readLn
                if (n >= length as)
                then do
                    putStrLn "Invalid selection"
                    select
                else return n
            select
        respond choice

Вместо возврата выбранного значения вы возвращаете Int, соответствующий индексу выбранного элемента. Остальное просто использует ExistentialQuantification.

Как рекомендовали другие, я предлагаю вам на самом деле просто отправить список Strings вместо того, чтобы использовать трюк экзистенциальной квантификации, но я включил его только для того, чтобы показать, как это будет сделано, на случай, если вам будет любопытно.

person Gabriel Gonzalez    schedule 07.07.2013