Skip to content

Instantly share code, notes, and snippets.

@ploeh
Last active December 2, 2022 14:11
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ploeh/6d8050e121a5175fabb1d08ef5266cd7 to your computer and use it in GitHub Desktop.
Save ploeh/6d8050e121a5175fabb1d08ef5266cd7 to your computer and use it in GitHub Desktop.
Helpful functions for working with pairs in F#
module Tuple2
let replicate x = x, x
let curry f x y = f (x, y)
let uncurry f (x, y) = f x y
let swap (x, y) = (y, x)
let mapFst f (x, y) = f x, y
let mapSnd f (x, y) = x, f y
let extendFst f (x,y) = f (x,y), y
let extendSnd f (x,y) = x, f(x,y)
let optionOfFst f (x, y) =
match f x with
| Some x' -> Some (x', y)
| None -> None
let optionOfSnd f (x, y) =
match f y with
| Some y' -> Some (x, y')
| None -> None
@ploeh
Copy link
Author

ploeh commented Sep 9, 2016

In F#, it's convenient to compose functions together. The output of one function becomes the input of the next function. Often, the type of the data flowing from one to the next function is a tuple, and most of the time, it's a pair (a tuple with two elements).

Sometimes, you need to translate a pair from one form into another. The Tuple2 module contains a small set of functions I often find useful when working with pairs.

@ploeh
Copy link
Author

ploeh commented Sep 12, 2016

As an example, imagine that you want to count all the even and odd numbers in a list of numbers. You can use Tuple2.mapSnd to map the second element of a grouping tuple to count the number of occurrences in each group:

> numbers |> List.groupBy isEven;;
> val it : (bool * int list) list =
  [(true,
    [38; 24; 48; 52; 26; 70; 74; 84; 74; 84; 74; 18; 4; 74; 34; 66; 50; 20; 76;
     40; 44; 44; 64; 46; 64; 50; 74; 96; 90; 92; 48; 8; 88; 96; 18; 86; 54; 40;
     58; 14; 84; 64; 86]);
   (false,
    [35; 55; 85; 61; 13; 99; 65; 35; 63; 13; 9; 9; 13; 91; 57; 87; 31; 1; 63;
     23; 63; 3; 11; 57; 85; 87; 39; 51; 47; 47; 17; 63; 67; 65; 55; 43; 13; 71;
     99; 11; 97; 59; 65; 55; 15; 35; 43; 67; 29; 87; 47; 3; 35; 37; 15; 73; 91])]
> numbers |> List.groupBy isEven |> List.map (Tuple2.mapSnd List.length);;
>
val it : (bool * int) list = [(true, 43); (false, 57)]

In this particular example, you could most likely come up with a simple and more efficient implementation, but I find the functions in the Tuple2 module handy for ad-hoc composition.

@ImaginaryDevelopment
Copy link

Can you add an example of where the extend functions are really useful? The rest I've already got lots of places I can think of where I would have used them.

@ploeh
Copy link
Author

ploeh commented Sep 17, 2016

I got the extend functions from Jérémie Chassaing: https://twitter.com/thinkb4coding/status/775427149243224064

@brianberns
Copy link

This one is also useful if a and b are of the same type:

let map f (a, b) = f a, f b

@ImaginaryDevelopment
Copy link

I typically call that one mapBoth @brianberns

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment