Ocaml 4.10.0 List.find_map

Can someone provide me with a simple/correct example of how find_map works?

This is how I think it works… Isn’t it the same as the code below?:

let lst = [('a', 2); ('b', 4); ('c', 6); ('d', 8); ]

let ans =
  Option.map (fun (_, y) -> y)
    (List.find_opt (fun (x, y) -> x = 'c') lst)

Let’s do a bit of type puzzle. find_map has type :

 ('a -> 'b option) -> 'a list -> 'b

This version raises an exception if the first argument f returns None for every element in the iterated list. If we want to stay pure, we could give find_map the type :

 ('a -> 'b option) -> 'a list -> 'b option

This one is a total function, it just returns None if f has returned None for all elements of l.
Then Option.map has type :

('a -> 'b) -> 'a option -> 'b option

and find_opt has type :

('a -> bool) -> 'a list -> 'a option

By working out the types, you see you can get a function with a type quite similar to find_map by composing find_opt and map this way :

let mystery predicate f l = 
    List.find_opt predicate l
    |> Option.map f
type mystery = ('a -> bool) -> ('a -> 'b) -> 'a list -> 'b option

This function just selects an element returning true for predicate, then applies f to it if it finds one.

Actually find_map just allows you to select and transform your element on the fly.
So this same mystery function could be expressed this way :

let mystery p f l = List.find_map (fun x -> if p x then Some (f x) else None) l

Hope it helps :slight_smile:

2 Likes

List.find_map allows you to return intermediate values that you could compute in the predicate function, for example:

type t = A of int | B of int | C of int

let lst = [ A 2; B 4; C 6 ]

let ans =
  List.find_map (fun t ->
      match t with
      | C y -> Some y
      | _ -> None
    ) lst

With find_opt, the result of the match would need to be discarded and computed again:

let ans =
  Option.map (fun t ->
      match t with
      | C y -> y
      | _ -> assert false
    )
    (List.find_opt (fun t ->
        match t with
        | C _ -> true
        | _ -> false
      ) lst)

Regarding your specific example, I guess you’re looking for something like :

let ans lst = List.find_map (fun (x, y) -> if x = 'c' then Some y else None) lst

This is what I was thinking… find_map is just a more flexible find_opt in that it can transform the element found.

1 Like