# Question about tuples and memory

**URL:** <https://discuss.ocaml.org/t/question-about-tuples-and-memory/10681>\
**Category:** Learning\
**Created:** [October 23, 2022, 1:07am UTC](https://discuss.ocaml.org/t/question-about-tuples-and-memory/10681 "2022-10-23T01:07:27Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![davidbruce](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/davidbruce/32/4014_2.png) [@davidbruce](https://discuss.ocaml.org/u/davidbruce)\
**Post date:** [October 23, 2022, 1:07am UTC](https://discuss.ocaml.org/t/question-about-tuples-and-memory/10681/1 "2022-10-23T01:07:27Z")

</div>

In the below code there are two functions that are doing the same thing. With a single argument “match function” there is some syntactic sugar with `= function`. You can use this with a tuple to shorten a function and it made me wonder if there were any performance penalties with this route.

I skimmed the article in Real World OCaml and it looks like tuples are compiled down into C arrays which would mean that they are heap allocated right? Unless you already have a tuple, wouldn’t you want to always opt for something like `type_effectiveness ` over `type_effectiveness_tuples`?

```ocaml
type ptype = TNormal | TFire | TWater

type effectiveness = ENormal | ENotVery | ESuper 

let effectiveness_multiplier : effectiveness -> float = function
  | ENormal -> 1.0
  | ENotVery -> 0.5
  | ESuper -> 2.0

let type_effectiveness (user_type : ptype) (target_type : ptype) =
  match user_type, target_type with
    | (TFire, TFire) | (TWater, TWater) | (TFire, TWater) -> ENotVery
    | (TWater, TFire) -> ESuper
    | _ -> ENormal

let type_effectiveness_tuples : ptype * ptype -> effectiveness = function 
  | (TFire, TFire) | (TWater, TWater) | (TFire, TWater) -> ENotVery
  | (TWater, TFire) -> ESuper
  | _ -> ENormal

```

Also in `type_effectiveness` is there a difference between:

1. 

```auto
  match user_type, target_type with
    | (TFire, TFire) | (TWater, TWater) | (TFire, TWater) -> ENotVery
    | (TWater, TFire) -> ESuper
    | _ -> ENormal

```

1. 

```ocaml
  match user_type, target_type with
    | TFire, TFire | TWater, TWater | TFire, TWater -> ENotVery
    | TWater, TFire -> ESuper
    | _ -> ENormal

```

I find 1 much easier to read.

---

<div class="post-metadata">

**Author:** ![crackcomm](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/crackcomm/32/3201_2.png) [@crackcomm](https://discuss.ocaml.org/u/crackcomm)\
**Post date:** [October 23, 2022, 2:07am UTC](https://discuss.ocaml.org/t/question-about-tuples-and-memory/10681/2 "2022-10-23T02:07:07Z")

</div>

My intuition is yes, there is slight performance penalty for making a copy into a tuple.  
The compiler explorer seems to [confirm](https://godbolt.org/z/sWjnhbr6v) my suspicion.

> [@davidbruce](#):
>
> I find 1 much easier to read.

There is no difference between the two.

---

<div class="post-metadata">

**Author:** ![gasche](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gasche/32/4_2.png) [@gasche](https://discuss.ocaml.org/u/gasche)\
**Post date:** [October 23, 2022, 3:26pm UTC](https://discuss.ocaml.org/t/question-about-tuples-and-memory/10681/3 "2022-10-23T15:26:16Z")

</div>

I’m not sure exactly which question you are asking, and which question @crackcomm is answering with his code.

1. `match foo, bar with p1, p2 -> ...` is syntactically equivalent to `match (foo, bar) with (p1, p2) -> ...`. The code suggests that a tuple is constructed and then deconstructed, but the match compiler is in fact careful to compile this into a parallel match on the inputs, so there is no actual tuple construction there.

2. `let f1 x y = ...` and `let f2 (x, y) = ...` have different semantics, but the compiler tries to hide any performance gap between the two. The second is a “tupled” function (all arguments are in a big tuple) that is a specifically recognized form, and calls with a direct tuple `f2 (foo, bar)` will in fact be compiled without a tuple creation again.

3. Some forms of tuple arguments will still allocate a tuple on the heap. But note that due to the generational GC, short-lived tuples allocated on the heap are very cheap, almost as cheap as allocating on the stack. (In particular if the function is defined as `let f (x, y) ... =`, then the tuple is deconstructed on entry and becomes dead on arrival, so it is short-lived.) In some performance- or latency-critical scenarios people start worrying about not running the minor GC too often, and then they avoid short-lived allocations, but this is certainly not the typical OCaml programming style and not necessary to get good performance for OCaml programs.
