Skip to content

Latest commit

History

History
102 lines (88 loc) 路 2.49 KB

100-named-args-functions.md

File metadata and controls

102 lines (88 loc) 路 2.49 KB

100-Named-Args Functions

Normally, when I see that a function is growing in number of arguments, like this:

let purchase_item:
  (
    ~quantity: int,
    ~user_id: User.id=?,
    ~cart_id: Cart.id=?,
    ~purchased_at: Datetime.t,
    ~should_email: Email_preference.t=?
    ~history_status: Purchase_history.Status.t=?,
    ~trace: Tracer.t=?,
    Item.t
  ) =>
  result(_, _);

I start to wonder if there's some datatype hidden in those parameters that should be discovered.

For example, perhaps we want to put together a purchase order that includes some of the purchase information:

module Purchase_order: {
  type t;
  let make: (~purchased_at: DateTime.t=?, ~quantity: int, Item.t) => t;
} = {
  type t = {
    purchased_at: DateTime.t,
    quantity: int,
    item: Item.t,
  };
  let make = (~purchased_at, ~quantity, item) => /* ... */ ();
};

And also a purchase preferences value that includes settings such as whether an email should be sent, or if this purchase should be hidden from history:

module Purchase_preference: {
  type t;
  let make:
    (
      ~should_email: Email_preference.t=?,
      ~history_status: Purchase_history.Status.t=?
    ) =>
    t;
} = {
  type t = {
    should_email: Email_preference.t,
    history_status: Purchase_history.Status.t,
  };
  let make = (~should_email, ~history_status) => /* ... */ ();
};

The user and cart seem tied together, and could perhaps live in a shopping session value that tells you what user and what cart are being used:

module Shopping_session: {
  type t;
  let make: (~user: User.t, ~cart: Cart.t) => t;
} = {
  type t = {
    user: User.t,
    cart: Cart.t,
  };
  let make = (~user: User.t, ~cart: Cart.t) => {
    {user, cart};
  };
};

Finally we can tie all of these new values together and our purchase_item function looks like this:

let purchase_item:
  (
    ~trace: Tracer.t=?,
    ~sess=Shopping_session.t,
    ~prefs=Purchase_preference.t,
    Purchase_order.t
  ) =>
  result(_, _);

Which I'd argue is much easier to digest, and lets me choose whether I want to know more about the shopping session, the preferences of the purchase, or the order itself.

Keep in mind that these two approaches have very different runtime characteristics, and if performance or memory are constrained for your use case you may need to live with functions that have plenty of arguments.