I often find myself using the |>
operator when constructing functions to pass as parameters to methods like List.find
. For example, I have this code snippet:
let parse_test_header (header : string list) : string * (string list) =
let is_attr (line : string) : bool = ':' == get line 0 in
let test_name = List.find (fun line -> line |> is_attr |> not) header in
let test_attrs = List.filter is_attr header in
(test_name, test_attrs)
For simplicity I would like to use |>
without having to wrap it in a fun ... -> ...
first, something like:
let parse_test_header (header : string list) : string * (string list) =
let is_attr (line : string) : bool = ':' == get line 0 in
let test_name = List.find (is_attr |> not) header in
let test_attrs = List.filter is_attr header in
(test_name, test_attrs)
However, this gives
31 | let test_name = List.find (is_attr |> not) header in
^^^^^^^
Error: This expression has type string -> bool
but an expression was expected of type bool
Is there some way to accomplish this?
Consider how |>
is implemented:
let ( |> ) x f = f x
You're passing is_attr
as the first argument, which is a function, which means not
would need to take a function as its argument. It doesn't so you get a type mismatch.
You're looking for function composition which can be tricky when used to compose polymorphic functions due to the value restriction, but can be implemented. Let's call this operator |.
.
let ( |. ) f g x = g (f x)
Now:
let parse_test_header (header : string list) : string * (string list) =
let is_attr (line : string) : bool = ':' == get line 0 in
let test_name = List.find (is_attr |. not) header in
let test_attrs = List.filter is_attr header in
(test_name, test_attrs)
Note that in your is_attr
function you're using physical equality (==
) rather than structural
equality (=
). This may not always do what you expect.
You're probably also looking for List.partition
. The composition of is_attr
and not
is no longer necessary.
let parse_test_header (header : string list) : string * (string list) =
let is_attr (line : string) : bool = ':' = get line 0 in
let (test_attrs, test_name) = List.partition is_attr header in
(List.hd test_name, test_attrs)
Additionally, is_attr
can be defined in terms of String.starts_with
.
let parse_test_header (header : string list) : string * (string list) =
let is_attr = String.starts_with ~prefix: ":" in
let (test_attrs, test_name) = List.partition is_attr header in
(List.hd test_name, test_attrs)
Now, |.
associates left to right. This means we can compose similarly to the way |>
works.
fun x -> x |> f |> g |> h
Is equivalent to:
f |. g |. h
If you wished to emulate Haskell's .
operator for composition, you'd want right-to-left associativity. Associativity of OCaml operators is governed by the first character in the operator.
fun x -> f @@ g @@ h x
Is equivalent to:
f @. g @. h
Where @.
is defined:
let ( @. ) f g x = f (g x)