I keep getting the below error when I run dune build
:
File "lib/ip2locationio.ml", line 25, characters 73-80:
25 | let uri = Uri.of_string ("https://api.ip2location.io/?key=" ^ config.api_key ^ "&format=" ^ config.format ^ "&source=" ^ config.source ^ "&source_version=" ^ config.source_version ^ "&ip=" ^ ip ^ lang_str) in
^^^^^^^
Error: Unbound record field api_key
I have looked through the other posts with similar issues and I've provided the signature in the .mli file but still unable to fix the issue.
My ip2locationio.ml file
open Lwt
open Cohttp
open Cohttp_lwt_unix
open Yojson
module Configuration = struct
type config = {
source_version : string;
api_key : string;
format: string;
source: string
}
let init api_key = {
source_version = "1.0.0";
api_key = api_key;
format = "json";
source = "sdk-ocaml-iplio"
}
end
module Ip_geolocation = struct
let call_api config ip lang =
let lang_str = if lang == "" then "" else "&lang=" ^ lang in
let uri = Uri.of_string ("https://api.ip2location.io/?key=" ^ config.api_key ^ "&format=" ^ config.format ^ "&source=" ^ config.source ^ "&source_version=" ^ config.source_version ^ "&ip=" ^ ip ^ lang_str) in
Lwt_main.run begin
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 ->
return (code, json_string)
)
end
(** Call the API to get geolocation info *)
let lookup config ip lang =
let code, json_string = call_api config ip lang in
let json = Basic.from_string json_string in
(code, json)
end
My ip2locationio.mli file
module Configuration :
sig
type config = {
source_version : string;
api_key : string;
format: string;
source: string;
}
val init : string -> config
end
module Ip_geolocation :
sig
val lookup :
Configuration.config -> string -> string -> int * Yojson.Basic.t
end
I would be very grateful if anyone can point me in the right direction. I am not sure what else I'm missing.
Let's consider a simpler example that replicates your error.
# module A = struct
type t = {x : int}
end;;
module A : sig type t = { x : int; } end
# module B = struct
let foo c = c.x
end;;
Error: Unbound record field x
# module C = struct
let foo (c : A.t) = c.x
end;;
module C : sig val foo : A.t -> int end
Attempting to create a new A.t
value runs into similar issues.
# A.{x = 5};;
- : A.t = {A.x = 5}
# {A.x = 7};;
- : A.t = {A.x = 7}
# {x = 4};;
Error: Unbound record field x
A straightforward resolution of this issue would be to explicitly type the function argument.
let call_api (config : Configuration.config) ip lang =
As a sidenote, consider the following line from your code:
let lang_str = if lang == "" then "" else "&lang=" ^ lang in
==
in OCaml checks for physical equality: are these two values literally the same value? However, =
checks for structural equality: are these two values equal?
You likely wanted:
let lang_str = if lang = "" then "" else "&lang=" ^ lang in
You may also find it advantageous for readability to use Printf.sprintf
in place of numerous string concatenations.
"https://api.ip2location.io/?key=" ^ config.api_key ^ "&format=" ^ config.format ^ "&source=" ^ config.source ^ "&source_version=" ^ config.source_version ^ "&ip=" ^ ip ^ lang_str
Becomes:
Printf.sprintf
"https://api.ip2location.io/?key=%s&format=%s&source=%s&source_version=%s&ip=%s%s"
config.api_key config.format config.source
config.source_version ip lang_str