Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is an "optionalized" pipe operator idiomatic F#

I like to use the pipe operator '|>' a lot. However, when mixing functions that return 'simple' values with functions that return 'Option-Typed-values', things become a bit messy e.g.:

// foo: int -> int*int
// bar: int*int -> bool
let f (x: string) = x |> int |> foo |> bar

works, but it might throw a 'System.FormatException:...'

Now assume I want to fix that by making the function 'int' give an optional result:

let intOption x = 
    match System.Int32.TryParse x with
    | (true, x) -> Some x
    | (false,_) -> None

Only problem now is that of course the function

let g x = x |> intOption |> foo |> bar

won't compile due to typing errors. Ok, simply define an 'optionalized' pipe:

let ( |= ) x f = 
   match x with
   | Some y -> Some (f y)
   | None -> None

now I can simply define:

let f x = x |> intOption |= foo |= bar

and everything works like a charme.

OK, question: Is that idiomatic F#? Acceptable? Bad style?

Remark: Of course, given the right types the '|=' operator allows to split and merge 'pipelines' with options at will while only care about options where they matter:

x |> ...|> divisionOption |= (fun y -> y*y) |=...|>...
like image 703
D.F.F Avatar asked Nov 19 '15 14:11

D.F.F


People also ask

What is the symbol for the pipeline operator?

The pipeline operator in JavaScript uses a very specific syntax. It uses this "pipe"|> symbol. In order for this operator to be used, it must be placed between the value you wish to pass to a function call and the function you wish to call.

What are pipe operators?

What is the Pipe Operator? The pipe operator is a special operational function available under the magrittr and dplyr package (basically developed under magrittr), which allows us to pass the result of one function/argument to the other one in sequence. It is generally denoted by symbol %>% in R Programming.

What is a forward pipe operator?

Title A Forward-Pipe Operator for R. Version 2.0.3. Description Provides a mechanism for chaining commands with a new forward-pipe operator, %>%. This operator will forward a value, or the result of an expression, into the next function call/expression.


2 Answers

I think using Option.map would be more idiomatic:

let g x = x |> intOption |> Option.map foo |> Option.map bar

like image 111
Giacomo Citi Avatar answered Sep 30 '22 13:09

Giacomo Citi


There are two aspects not yet covered by other answers.

  • Monadic operations for F#'s Option type
  • Judicious use of custom operators instead of pipelining to standard functions can improve readability

Instead of a full-blown computation expression like MaybeBuilder(), we can define let-bound functions providing monadic operations for the Option type. Let's represent the bind operation by the operator >>=:

let (>>=) ma f = Option.bind f ma
// val ( >>= ) : ma:'a option -> f:('a -> 'b option) -> 'b option
let ``return`` = Some
// val return : arg0:'a -> 'a option

From this follows

let (>=>) f g a = f a >>= g
// val ( >=> ) : f:('a -> 'b option) -> g:('b -> 'c option) -> a:'a -> 'c option
let fmap f ma = ma >>= (``return`` << f)
// val fmap : f:('a -> 'b) -> ma:'a option -> 'b option
let join mma = mma >>= id
// val join : mma:'a option option -> 'a option

fmap is basically Opion.map; join un-nests a nested instance by one level, and composition by the Kleisli operator >=> is an alternative to pipelining.

In lightweight syntax, operators are exempted from increasing indentation with nested scope. This could be useful when stringing together lambda functions, allowing nesting while still indenting by at most one level.

a_option
|> Option.bind (fun a ->
    f a
    |> Option.bind (fun b ->
        g b 
        |> Option.bind ... ) )

vs

a_option
>>= fun a ->
    f a
>>= fun b ->
    g b
>>= ...
like image 30
kaefer Avatar answered Sep 30 '22 13:09

kaefer