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.
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