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

В С# я использую асинхронные версии 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 не определен, а его рекурсивная техническая маркировка неверна, поскольку это не рекурсивный метод?

Ответ 1

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

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

    // do some stuff with client here

    do! accept(t)
  }

Ответ 2

@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
    }

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