Module type and List.map


#1

Hi!
I was surprised that having User type defined in a module:

module User = {
  type t = {
    name:string,
    age:int
  }
};

and user list defined like this:

let users: list(User.t) = [];

to iterate over user list I can’t simply do:

users |> List.map(u => u.age);  // age field is not visible!

but instead I need to bring module into the current scope either through:

users |> List.map((u: User.t) => u.age);

or

users |> List.map(u => u.User.age); // this one looks strange :)

or

User.(users |> List.map(u => u.age));

Why it is needed since users has explicit type list(User.t) ?


#2

It’s because of the way modules work.

If you try to compile this code:

module User = {
  type t = {
    name:string,
    age:int
  }
};

let users: list(User.t) = [];

users |> List.map(u => u.age);

It won’t work, because the compiler will try to infer the type of u based on the code context. As there’s no type with age field on the current code context, the compiler will fail. Instead of using the alternatives that you wrote, you can simply open the module to the context:

module User = {
  type t = {
    name:string,
    age:int
  }
};

open User;

let users: list(t) = [];

users |> List.map(u => u.age);

This will work fine. You can read a little more about how modules works here.


#3

Yes, I know I can globally open the module - but it’s not recommended :slight_smile: I thought that compiler will infer the u type looking at the users declaration (like in other languages). But it seems ocaml type inferring works a little bit different or there is sth in List implementation I’m not aware of. I will need to dive in into docs more :slight_smile: Thx!


#4

Well, it’s also recommended to annotate functions types whenever possible. :slightly_smiling_face:


#5

Yeah it’s not recommended to open too much. Using Belt here means you don’t have to annotate: https://twitter.com/_chenglou/status/997612108501041152


#6

Thx. So it’s time to switch to Belt :slight_smile: