admin管理员组

文章数量:1126335

I am trying to write a simple executable code in OCaml below.

open Printf
open Lwt
open Cohttp
open Cohttp_lwt_unix
open Yojson

let () =
  let ip = "8.8.8.8" in
  let key = "" in
  let uri =
    Uri.of_string
      ("/?format=json&key=" ^ key ^ "&ip=" ^ ip)
  in
  Lwt_main.run
    ( Client.get uri >>= fun (resp, body) ->
      let code = resp |> Response.status |> Code.code_of_status in
      let json_promise = body |> Cohttp_lwt.Body.to_string in
      json_promise >>= fun json_string ->
      let json = Basic.from_string json_string in
      let open Yojson.Basic.Util in
      if code == 200 then
        if member "usage_type" json <> `Null then
          let usage_type = json |> member "usage_type" |> to_string in
          printf "usage_type: %s\n" usage_type
        else
          printf
            "ERROR: The usage_type field requires a paid subscription to the \
             Starter plan or higher."
      else if (code == 400 || code == 401) && member "error" json <> `Null then
        let error_message =
          json |> member "error" |> member "error_message" |> to_string
        in
        printf "ERROR: " ^ error_message
      else printf "HTTP Code: " ^ Int.to_string code )

But I keep seeing the below when I run dune build.

File "bin/main.ml", line 24, characters 10-46:
24 |           printf "usage_type: %s\n" usage_type
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: This expression has type unit but an expression was expected of type
         'a Client.io

From other StackOverflow posts, it seems to be related to the different types being returned in the if/else but I have made sure all of the if/else are using printf.

I would appreciate if anyone can let me know what else I'm doing wrong.

I am trying to write a simple executable code in OCaml below.

open Printf
open Lwt
open Cohttp
open Cohttp_lwt_unix
open Yojson

let () =
  let ip = "8.8.8.8" in
  let key = "" in
  let uri =
    Uri.of_string
      ("https://api.ip2location.io/?format=json&key=" ^ key ^ "&ip=" ^ ip)
  in
  Lwt_main.run
    ( Client.get uri >>= fun (resp, body) ->
      let code = resp |> Response.status |> Code.code_of_status in
      let json_promise = body |> Cohttp_lwt.Body.to_string in
      json_promise >>= fun json_string ->
      let json = Basic.from_string json_string in
      let open Yojson.Basic.Util in
      if code == 200 then
        if member "usage_type" json <> `Null then
          let usage_type = json |> member "usage_type" |> to_string in
          printf "usage_type: %s\n" usage_type
        else
          printf
            "ERROR: The usage_type field requires a paid subscription to the \
             Starter plan or higher."
      else if (code == 400 || code == 401) && member "error" json <> `Null then
        let error_message =
          json |> member "error" |> member "error_message" |> to_string
        in
        printf "ERROR: " ^ error_message
      else printf "HTTP Code: " ^ Int.to_string code )

But I keep seeing the below when I run dune build.

File "bin/main.ml", line 24, characters 10-46:
24 |           printf "usage_type: %s\n" usage_type
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: This expression has type unit but an expression was expected of type
         'a Client.io

From other StackOverflow posts, it seems to be related to the different types being returned in the if/else but I have made sure all of the if/else are using printf.

I would appreciate if anyone can let me know what else I'm doing wrong.

Share Improve this question edited Jan 9 at 7:04 Chris 36.3k5 gold badges31 silver badges54 bronze badges asked Jan 9 at 5:49 VlamVlam 1,7881 gold badge9 silver badges20 bronze badges 1
  • Small note: be careful about == vs. = – Chris Commented Jan 9 at 6:13
Add a comment  | 

2 Answers 2

Reset to default 2

I'd suspect this is due to >>=, whose type in Lwt is

(>>=) : 'a Lwt.t -> ('a -> 'b Lwt.t) -> 'b Lwt.t

On the left-hand side you're passing it the result of Client.get url, so the right-hand side must be a function returning the same type. Likewise, Lwt.run expects an Lwt.t. (I'm not intimately familiar with all the libraries involved, but Client.io presumably is a type synonym expanding to Lwt.t here.)

The fix for this example should be to use >|= instead of the second >>=, whose type is

(>|=) : 'a Lwt.t -> ('a -> 'b) -> 'b Lwt.t

Andreas Rossberg has identified one issue, but you're also seeing some operator precedence issues.

This kind of thing:

printf "ERROR: " ^ error_message

And this:

printf "HTTP Code: " ^ Int.to_string code

Look really suspicious, since printf "HTTP code: " evaluates to () with cannot be provided to ^. It looks like you meant to write something like:

printf ("HTTP Code: " ^ Int.to_string code)

But you might as well leverage printf to write the following, which is cleaner and has no operator precedence issues.

printf "HTTP Code: %d" code

Also beware opening several modules. If more than one defines the same operator or they redefine standard library operators you can end up with some unexpected behavior.

Aside from fully qualifying names, you can locally open modules, mitigating this issue. But since you've already used this once in your code, it seems you're aware of this option.

On a style note, I might just refactor this:

        let error_message =
          json |> member "error" |> member "error_message" |> to_string
        in
        printf "ERROR: " ^ error_message

To:

        json 
        |> member "error" 
        |> member "error_message" 
        |> to_string
        |> printf "ERROR: %s"

Also opinion, but there is an opportunity to rework:

      if code == 200 then
        if member "usage_type" json <> `Null then
          let usage_type = json |> member "usage_type" |> to_string in
          printf "usage_type: %s\n" usage_type
        else
          printf
            "ERROR: The usage_type field requires a paid subscription to the \
             Starter plan or higher."
      else if (code == 400 || code == 401) && member "error" json <> > `Null then
        let error_message =
          json |> member "error" |> member "error_message" |> to_string
        in
        printf "ERROR: " ^ error_message
      else printf "HTTP Code: " ^ Int.to_string code )

Using pattern-matching and conditional guards:

      match code with
      | 200 when member "usage_type" json <> `Null ->
        json
        |> member "usage_type" 
        |> to_string
        |> printf "usage_type: %s\n"
      | 200 ->
        printf
          "ERROR: The usage_type field requires a paid subscription to the \
           Starter plan or higher."
      | (400 | 401) when member "error" json <> `Null ->
        json 
        |> member "error" 
        |> member "error_message" 
        |> to_string
        |> printf "ERROR: %s"
      | _ -> printf "HTTP Code: %d" code

本文标签: ocamlThis expression has type unit but an expression was expected of type 39a ClientioStack Overflow