14

It seems that Ocaml batteries have comprehension syntax: http://en.wikipedia.org/wiki/List_comprehension#OCaml

However, what module should I include to use this syntax? I already open Batteries, but it doesn't work. Or is there a more idiomatic way to do list comprehension? I can use List.map and BatList.remove_if to achieve similar results, but that is much less elegant.

user69818
  • 403
  • 6
  • 13

2 Answers2

19

Currently there're two libraries in OCaml that provide list comprehension, one was formerly a part of OCaml Batteries, another is shipped with camlp4. Neither is widely used and I, personally, do no recommend you to use any.

For list comprehension to work, you need to change the syntax of the language. This can be done with preprocessing your program, written in an extended syntax, with a camlp4 preprocessor. Also, list comprehension is not a first class citizen in OCaml community, and it is not well supported by the modern toolkits. Although, you can still easily play with it in a toplevel, for that you need, to install the list comprehension package:

opam install pa_comprehension 

and load it into a toplevel, using the following directives:

# #use "topfind";;
# #camlp4o;;
# #require "pa_comprehension";;
# open Batteries;;
# [? 2 * x | x <- 0 -- max_int ; x * x > 3 ?];;

But again, my personal opinion that list comprehension is not the best way to structure your code.

Life without comprehension

The example, you provided, can be expressed using core_kernel Sequence module (an analog of the Batteries Enum)

let f n =
  Sequence.(range 0 n |>
            filter ~f:(fun x -> x * x > 3) |>
            map ~f:(fun x -> x * 2))

Hence a filter |> map is such a common idiom there exists a filter_map function:

let f n =
  Sequence.(range 0 n |>
            filter_map ~f:(fun x ->
                if x * x > 3 then Some (x * 2) else None))

You may notice, that this examples takes more code, than list comprehension. But as soon as your programs will start to mature from simple hello world applications with integers to something more sophisticated, you will agree that using explicit iterators is more readable and comprehensible.

Also, since libraries in Core are so consistent, you can use a simple List instead of Sequence just by substituting the latter by the former. But of course, List is eager, unlike the Sequence, so playing with max_int using lists is not a good idea.

Moreover, since all containers are monads, you can use monadic operators for mapping, like:

let odds n = List.(range 0 n >>| fun x -> x * 2 + 1)
ivg
  • 34,431
  • 2
  • 35
  • 63
  • So what would you recommend instead? – user69818 Dec 26 '14 at 04:45
  • For example, if I want to get something like (in python code) [x*2 for x in xlist if x>3], is there a clean way to do it? – user69818 Dec 26 '14 at 04:59
  • @user69818: Use `List.filter` for the `>3` conditions and then `List.map` – nimrodm Dec 26 '14 at 05:06
  • @nimrodm yea, it is similar to what I used (I used BatList.remove_if instead of List.filter though, but now I see filter is the right choice, as remove_if only removes the first element matched). – user69818 Dec 26 '14 at 05:28
  • @nimrodm However, as I mentioned in the question, this approach is much less elegant than list comprehension. – user69818 Dec 26 '14 at 05:29
  • 4
    @user69818: It isn't that bad if you have a pipe operator defined `|>` (see http://www.ocamlpro.com/blog/2012/08/20/ocamlpro-and-4.00.0.html "pipe operator") – nimrodm Dec 26 '14 at 08:20
  • @nimrodm the main nonelegant part is that I have to write, for instance, `fun x -> x>3`, where in list comprehsnive it is simply `x>3`, not to mention that `List.filter` is longer than `if`. – user69818 Dec 27 '14 at 01:36
  • @ivg thanks for the comprehensive answer. I have no idea what the last expression means. Can you direct me to some references on it (I've only heard of monad for Haskell)? – user69818 Dec 27 '14 at 01:42
  • 1
    The last expression is the same as `[ x * 2 + 1 for x in range(n) ]` in python. Monad can be seen as a generalization of the container, actually monadic map operator is exactly the same as a map operator for any other container. Here is a good [tutorial](http://webcache.googleusercontent.com/search?q=cache:WW--TtRXT2AJ:blog.enfranchisedmind.com/2007/08/a-monad-tutorial-for-ocaml/+&cd=1&hl=en&ct=clnk&gl=us) – ivg Dec 27 '14 at 16:14
  • @ivg working link for ocaml monad tutorial: https://web.archive.org/web/20071231141139/http://enfranchisedmind.com/blog/2007/08/06/a-monad-tutorial-for-ocaml/ – MaxPlankton Feb 16 '22 at 18:51
  • This is a rather ancient answer :) Since that time I have implemented the [monads][1] library that comes with its own tutorial, I hope you might also find it useful (both the tutorial and the library, which you can get with `opam install monads`). [1]: https://binaryanalysisplatform.github.io/bap/api/master/monads/Monads/Std/index.html – ivg Feb 16 '22 at 19:01
2

list comprehension is already included in standard ocaml

#require "camlp4.listcomprehension";;

[ x * x | x <- [ 1;2;3;4;5] ];;

- : int list = [1; 4; 9; 16; 25]
Marko Tunjic
  • 1,811
  • 1
  • 14
  • 15