Как сделать цепочечные обратные вызовы в F#?

В C# я использую асинхронные версии TcpListener/TcpClient и объединяю их в цепочку с помощью метода обратного вызова, чтобы после завершения обратного вызова было опубликовано другое сообщение Accept/Read. Вот пример (не проверено):

    public void Start()
    {
        TcpListener listener = new TcpListener(IPAddress.Any, 3000);
        listener.Start();

        PostAccept(listener);
    }

    private void PostAccept(TcpListener listener)
    {
        listener.BeginAcceptTcpClient(AcceptCallback, listener);
    }

    private void AcceptCallback(IAsyncResult ar)
    {
        var listener = ar.AsyncState as TcpListener;

        if (listener == null)
        {
            return;
        }

        // get the TcpClient and begin a read chain in a similar manner

        PostAccept(listener);
    }

У меня вопрос, как мне моделировать что-то подобное в F#? Буду ли я использовать ключевое слово async? Является ли код после BeginAcceptTcpClient, BeginRead и т. Д. По существу кодом, который будет выполняться в функции обратного вызова? Например, это правильно?

let accept(listener:TcpListener) = async {      
  let! client = listener.AsyncAcceptTcpClient()    
  // do something with the TcpClient such as beginning a Read chain
  accept(listener)
  return()
}    

Приведенный выше код не работает, потому что accept не определен, а техническая маркировка его рекурсивно неверна, так как это не рекурсивный метод?

2 ответа

Решение

Я не уверен, что вы имеете в виду под "это не рекурсивный метод"; если вы ссылаетесь на функцию из ее собственного определения, то это рекурсивная функция. У меня нет большого опыта работы с классами Sockets, но, возможно, что-то в этом роде - то, что вы ищете?

let rec accept(t : TcpListener) =
  async {
    let! client = t.AsyncAcceptTcpClient()

    // do some stuff with client here

    do! accept(t)
  }

Ответ @kvb правильный и идиоматический.

Просто хотел также отметить, что вы можете использовать (задыхаться) цикл:

let accept(t:TcpListener) =
    let completed = ref false 
    async {
        while not(!completed) do 
            let! client = t.AsyncAcceptTcpClient()
            if client <> null then
                Blah(client)
            else
                completed := true
    }

(это было набирать код в моем браузере, надеюсь, он компилируется). Это хорошо, так как вы, конечно, не можете использовать цикл в коде C# (который должен охватывать несколько методов с помощью обратных вызовов begin/end).

Другие вопросы по тегам