Is an “optional” pipe operator, idiomatic F #

I like to use the pipe operator '| > '. However, when mixing functions that return "simple" values ​​with functions that return "values ​​specified by a parameter", everything becomes a bit messy, for example:

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

works, but it can throw "System.FormatException: ..."

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

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

The only problem now is that of course the function

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

will not compile due to input errors. Ok, just define the "optional" channel:

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

Now I can simply determine:

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

and everything works like an enchantment.

OK, the question is: Is this an idiomatic F #? Acceptable? Bad style?

. , '| =' "" , , :

x |> ...|> divisionOption |= (fun y -> y*y) |=...|>...
+4
4

, Option.map :

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

+8

Option.map/Option.bind - , , , .

, , Option, , MaybeBuilder. :

type MaybeBuilder() =
    member this.Bind(m, f) = 
        Option.bind f m
    member this.Return(x) = 
        Some x
    member this.ReturnFrom(x) = 
        x

let maybe = MaybeBuilder()

:

maybe {
   let! a = intOption x
   let! b = foo a
   let! c = bar b
   return c
}
+2

, .

  • F # Option
  • .

, MaybeBuilder(), let-bound, Option. >>=:

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

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 Opion.map; join un- , Kleisli >=> .

. -, , .

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

a_option
>>= fun a ->
    f a
>>= fun b ->
    g b
>>= ...
+2

(|>) . - F # ( / ) . :

  • , Option.map Option.bind, . intOption |> Option.map foo |> Option.map bar , foo bar ;
  • ;
  • ( Lisp :)

, "" .
.. , - .


. , , :

MyApp.exe -source foo -destination bar -loglevel debug

... Map<string, string>, /.

loglevel , :

  • Map Key="loglevel"; , ;
  • , ,
  • enum loglevel, . . ;
  • , , , ;
  • , None. ;
  • , Some, Option.get.

. :

let logLevel =
    "loglevel"
    |> args.TryFind                            // (1)
    |> Option.bind      ^<| Seq.tryPick Some   // (2)
    |> Option.bind      ^<| fun strLogLevel -> // (3)
        match System.Enum.TryParse(strLogLevel, true) with
        | true, v -> Some v
        | _ -> None
    |> Option.Or        ^<| fun _ ->           // (4)
        if System.Diagnostics.Debugger.IsAttached then Some LogLevel.Debug else None
    |> Option.OrDefault ^<| fun _ ->           // (5)
        LogLevel.Verbose
    |> Option.get                              // (6)

Here we see how key ( "loglevel") is sequentially converted through a chain of "optional" calculations. Each lambda introduces its own alias for the converted value (for example, strLogLevel).


And here is the library to use:

// A high precedence, right associative backward pipe, more info here:
// http://tryfs.net/snippets/snippet-4o
let inline (^<|) f a = f a

/// <summary>Advanced operations on options.</summary>
type Microsoft.FSharp.Core.Option<'T> with
    // Attempts to return Some either from the original value or by calling lambda.
    // Lambda is required to return a monadic value (Option<'T>)
    static member Or f (x:Option<'T>) =
        match x with
        | None      -> f()
        | x         -> x

    // Same as above, but for lambdas returning plain types (e.g., `T)
    static member OrDefault f (x:Option<'T>) =
        match x with
        | None      -> f() |> Some
        | x         -> x
+1
source

Source: https://habr.com/ru/post/1616565/


All Articles