Ocaml Lwt_流的使用和设计问题

Ocaml Lwt_流的使用和设计问题,ocaml,Ocaml,我正在编写一个基于lwt的TwitterAPI库,我想使用lwt_流库实现TwitterAPI的功能 我决定使用Lwt\u stream.from并为该函数提供一个参数f 以下是我目前的工作 let get_follower ~access_token ~screen_name ?(count=5000) ?(wait=true) () = let base_uri = "https://api.twitter.com/1.1/followers/ids.json" in let cur

我正在编写一个基于lwt的TwitterAPI库,我想使用lwt_流库实现TwitterAPI的功能

我决定使用
Lwt\u stream.from
并为该函数提供一个参数
f

以下是我目前的工作

let get_follower ~access_token ~screen_name ?(count=5000) ?(wait=true) () =
  let base_uri = "https://api.twitter.com/1.1/followers/ids.json" in
  let cursor = ref (-1) in
  let f () =
    Client.do_get_request
      ~uri_parameters:
        ["screen_name", screen_name;
         "cursor", (string_of_int (!cursor));
         "count", (string_of_int count)]
      ~uri:(Uri.of_string base_uri)
      ~access_token:access_token
    () >>= fun res ->
    match res with
    | Ok (header, str) -> begin
      match (Yojson.Safe.from_string str |> user_ids_of_yojson) with
      | `Ok uids -> 
        cursor := uids.next_cursor;
        return (Some (Ok uids))
      | `Error msg -> failwith msg 
      end
    | Error e -> return (Some (Error (process_error_exn e))) in
  Lwt_stream.from f
我不确定是否应该使用
ref
我使用
ref
的原因是
f
的行为取决于它之前返回的值。
具体来说,下次将使用的
光标的值取决于当前
下一个光标
,如果
光标
为零,
f
知道它到达末尾并返回
None


在这里使用
ref
被认为是一个好的设计选择吗?有没有更好的方法来实现此功能

因为
f
需要
单位
并且每次都会产生不同的结果,正如你所说的,它必须依赖于某种状态,正如我想你已经意识到的那样。它已经通过依赖I/O的结果实现了这一点,我认为这就是为什么
ref
的问题不容易回答的原因。否则,答案将是肯定的,这是必要的(见下文第(2)点)

我认为有两种主要的可能性可以消除语法上的
ref
,但它们都没有意义

  • 以某种方式将这些状态位隐藏在I/O
    f
    已经在做的事情中,即在API的对等方上。这似乎是不可能的,事实上,看起来这些位必须位于客户端,API才能正常工作
  • 将这些状态位隐藏在客户端的其他位置。但是在某种程度上,在客户机的某个地方仍然会有精神上等同于
    ref
    的东西。您可以编写或使用某种包装器来分析这个
    ref
    ,但是除非这个包装器在很多地方都有用,否则它只会使这里存在额外的客户端状态变得不那么明显。我更愿意让州保持地方性,尽可能接近它的用途。既然
    f
    已经不可避免地做了“肮脏”的事情,
    f
    是正确的地方
  • 总之,我要说保持
    ref
    的原样。不管怎样,流都是有状态的


    以上讨论假设您必须使用
    Lwt\u流
    。如果没有,您可以提供一个替代接口,其类型类似于
    get\u follower:cursor:int->…->(results*cursor)Lwt.t
    ,并让调用者在必要时担心状态。这可能是我首先要尝试的

    编辑


    当然,这样做有一个缺点,那就是可能会编写调用代码来提供错误的游标。您可以通过返回部分应用的函数来隐藏光标,但是调用代码可能是为了调用错误的函数而编写的。没有线性类型,如果您担心这些可能性,流方法更安全,因此它有它的优势。

    因为
    f
    采用
    单位
    ,并且每次产生不同的结果,正如您所说,它必须依赖于某些状态,我想您已经意识到。它已经通过依赖I/O的结果实现了这一点,我认为这就是为什么
    ref
    的问题不容易回答的原因。否则,答案将是肯定的,这是必要的(见下文第(2)点)

    我认为有两种主要的可能性可以消除语法上的
    ref
    ,但它们都没有意义

  • 以某种方式将这些状态位隐藏在I/O
    f
    已经在做的事情中,即在API的对等方上。这似乎是不可能的,事实上,看起来这些位必须位于客户端,API才能正常工作
  • 将这些状态位隐藏在客户端的其他位置。但是在某种程度上,在客户机的某个地方仍然会有精神上等同于
    ref
    的东西。您可以编写或使用某种包装器来分析这个
    ref
    ,但是除非这个包装器在很多地方都有用,否则它只会使这里存在额外的客户端状态变得不那么明显。我更愿意让州保持地方性,尽可能接近它的用途。既然
    f
    已经不可避免地做了“肮脏”的事情,
    f
    是正确的地方
  • 总之,我要说保持
    ref
    的原样。不管怎样,流都是有状态的


    以上讨论假设您必须使用
    Lwt\u流
    。如果没有,您可以提供一个替代接口,其类型类似于
    get\u follower:cursor:int->…->(results*cursor)Lwt.t
    ,并让调用者在必要时担心状态。这可能是我首先要尝试的

    编辑


    当然,这样做有一个缺点,那就是可能会编写调用代码来提供错误的游标。您可以通过返回部分应用的函数来隐藏光标,但是调用代码可能是为了调用错误的函数而编写的。没有线性类型,如果您担心这些可能性,流方法更安全,因此它有它的优势。

    因为
    f
    采用
    单位
    ,并且每次产生不同的结果,正如您所说,它必须依赖于某些状态,我想您已经意识到。它已经通过依赖I/O的结果实现了这一点,我认为这就是为什么
    ref
    的问题不容易回答的原因。否则,答案将是肯定的,这是必要的(见下文第(2)点)

    我认为有两种主要的可能性可以消除语法上的
    ref
    ,但它们都没有意义

  • 以某种方式将这些状态位隐藏在I/O
    f
    已经在做的事情中,即在API的对等方上。这似乎是不可能的,事实上,看起来A的位必须在客户机上
    let get_follower ~access_token ~screen_name ?(count=5000) ?(wait=true) () =
      let base_uri = "https://api.twitter.com/1.1/followers/ids.json" in
      let make_request cursor = 
        Client.do_get_request
          ~uri:(Uri.of_string base_uri) ~access_token    
          ~uri_parameters:[
            "screen_name", screen_name;
            "cursor", string_of_int cursor;
            "count", string_of_int count
          ] () in 
      let parse_response = function
        | Error e -> Error (process_error_exn e)
        | Ok (header, str) -> 
          match Yojson.Safe.from_string str |> user_ids_of_yojson with
          | `Ok uids -> Ok uids
          | `Error msg -> failwith msg in
      let stream,push = Lwt_stream.create () in
      let rec loop cursor = 
        make_request cursor >|= parse_response >>= function
        | Ok {next_cursor=0} -> push None; return_unit
        | Ok {next_cursor=n} as r -> push (Some r); loop n
        | error -> push (Some error); loop cursor in
      async (fun () -> loop (-1));
      stream
    
     module Follower : sig
       type t
       val name : t -> string Lwt.t
       ...
     end