Antrikshy
Antrikshy

Reputation: 3106

How do I split an OCaml list into head and tail?

I'm just learning OCaml and right now, I use match l with h::t -> to split lists when I need to access the head. But I'm sure there has to be a better way to split a list this way. I can't seem to find much online. Maybe I'm not looking in the right way.

Upvotes: 4

Views: 9919

Answers (3)

mookid
mookid

Reputation: 1172

The OCaml way for this issue is the expression

match l with
| [] -> some_expr
| x :: xs -> some_other_expr
  • The worst solution is using List.hd or List.hd, leading to possible runtime exception.

  • The second worse solution is an incomplete match such as match l with x :: xs leading also to the same runtime errors, but you at least get a compile time warning, with the matching case(s) you missed.

Note that you can ignore some parts of the matching if not used, so your code is self documenting:

let hd_option lst =
match lst with
| [] -> None
| x :: _ -> Some x (* the tail is not used, so I just don't name it *)

Upvotes: 1

dfeuer
dfeuer

Reputation: 48581

I don't know OCaml, but Haskell offers these, which you can probably translate easily:

-- I think ML might write this signature
-- uncons : 'a list -> ('a * 'a list) option
uncons :: [a] -> Maybe (a, [a])
uncons (x : xs) = Just (x, xs)
uncons [] = Nothing

maybe :: b -> (a -> b) -> Maybe a -> b
maybe def f Nothing = def
maybe def f (Just x) = f x

Then you can write things like

drop1 :: [a] -> [a]
drop1 = maybe [] snd . uncons

Upvotes: -2

camlspotter
camlspotter

Reputation: 9030

It's the best way.

There are functions List.hd and List.tl to get hd and tl but there is no function available in the standard library to get the head and tail in one move. In addition List.hd and List.tl are unsafe: if you give an empty list to them, they raise runtime exceptions:

# List.hd [];;
Exception: Failure "hd".

You can use them safely if you are 100% sure that the argument cannot be empty, but your speculation is often not correct in practice. List.hd and tl can be very good sources of bugs.

Using patterns with let: let (h::t) = l in ... is possible, but it also has a risk of runtime failures, but the compiler can tell you that your pattern is not exhaustive and lacks the handling of [].

match can have multiple cases and you can cover the case for []: match l with [] -> ... | (h::t) -> .... If you forget the case for [], the compiler can warn you about it.

Upvotes: 8

Related Questions