Decifering Type Error message


#1

Im looking at @Khady’s bs-dotenv repo and trying to return an env value to pass to variable in some file.

The [demo] (https://github.com/ahrefs/bs-dotenv#example) there, which works, is
.env

VALUE1=demo1
VALUE2=demo2

Dotenv.config();

let _ = Js.log(Node.Process.process##env); 

then run nodejs src/demo.bs.js | grep demo.

How do i parse some specific key off of the process##env?

I figured something like this would work,

let value = Node.Process.process##env |> Dotenv.parseString("VALUE1");

but it throws this error:

Module build failed: Error: We've found a bug for you!  /Users/prisc_000/code/REASON/EXPLORINGReML/re-auth0/src/demo.re 9:38-65

  7 │ /* Js.log(value1); */
  8 │ /* let _ = Js.log(x.VALUE1); */  9 │ let y = Node.Process.process##env |> Dotenv.parseString("VALUE1");

  This has type:
    Js.Dict.t(string) (defined as Js.Dict.t(string))
  But somewhere wanted:    Js.Dict.t(string) => 'a

What does this error mean and how do you fix it?

Please feel free to dumb it down. There is always something to learn that you might not know you are teaching. Thank you.


#2

The parseString and parseBuffer functions are from dotenv internals. They are described here: https://github.com/motdotla/dotenv#parse

I don’t think those functions are doing what you expect. You want to use them if you have a string containing what would usually be in a .env file and load that.

The interesting part in the error message is:

  This has type:
    Js.Dict.t(string) (defined as Js.Dict.t(string))

It is telling you that Node.Process.process##env is of type Js.Dict.t(string). This type is documented here in bucklescript documentation: https://bucklescript.github.io/docs/en/object.html#object-as-hash-map and https://bucklescript.github.io/bucklescript/api/Js.Dict.html

So to get values from the env, you have to use Js.Dict.get or Js.Dict.unsafeGet.

Dotenv.config();

let _ = Js.log(Node.Process.process##env); 
let value1 = Js.Dict.unsafeGet("VALUE1");
let value2 = Js.Dict.unsafeGet("VALUE2");

#3

Most certainly was not doing what I expected. Thanks for the detailed explanation on Js.Dict and the links.

So the big return for me on this is if I see an error saying something about 'this has type"… then that means that I may be calling the something incorrectly. I can check the type for that function to see what the available methods are for calling something for that type. So in this case, I would open up Js.Dict docs to see what is available there. Here that is Js.Dict.get and Js.Dict.unsafeGet so if i expect to get anything at all it would have to be with one of those methods. Reading the docs, I see why you chose Js.Dict.unsafeGet since we know we put the env variables there.

Also of note, the error also says:

But somewhere wanted:
    Js.Dict.t(string) => 'a

Why did maintainers put that there? Reading @teberl’s link i get that

“Prefixed apostrophes are reserved for type variables (think generic types in C-style languages)”

So in the Js.Dict docs the main type is defined as type 'a t or a variable of type t so when I get to that part of the error its telling that whatever i’m passing in has to be of that type, which the docs say has been defined as:

Dictionary type (ie an ‘{ }’ JS object). However it is restricted to hold a single type; therefore values must have the same type.
This Dictionary type is mostly used with the Js_json.t type.

It not yet super clear to me how it related to resolving the error but there is good information there.

Working solution

Finally, the working solution, following your guidance and the examples in the Js.Dict docs and bucklescript docs, is:

//demo.re
Dotenv.config();

let envMap = Node.Process.process##env;
let value1 = Js.Dict.unsafeGet(envMap, "VALUE1");

let value2 = Js.Dict.unsafeGet(envMap, "VALUE2");

Js.log2("Value1: ", value1);

Js.log2("Value2: ", value2);

The docs tell us we have to pass the dictionary, example:

Array.iter (fun key -> Js.log (Js_dict.unsafeGet dic key)) (Js_dict.keys dict) 

Here that the dict is Node.Process.process##env

@Khady is expected that your code should return the value? It throws this error when i run it:

  11 │ let value1 = Js.Dict.unsafeGet("VALUE1");  12 │  13 │ let value2 = Js.Dict.unsafeGet("VALUE2");

  This has type:
    string
  But somewhere wanted:
    Js.Dict.t('a) (defined as Js.Dict.t('a))

@Khady, thank so much for taking the time. That was immensely helpful.


#4

I forgot an argument of unsafeGet while typing the example. Correct one should be Js.Dict.unsafeGet(Node.Process.process##env, "VALUE1").


#5

I tried to call that module from a different module and everything comes back undefined.
Any idea why that might be?