Reputation: 27
I am looking at the F# syntax and I wonder if there is any operator to access to a struct field when the struct is optional and assign assign None to the expression the original struct was optional. For example a way to shorten the first line into the second:
type Post = { picture: string }
let post:Post option = None
let maybeImage = if post <> None then Some post.Value.picture else None
let maybeImage = Option.bind (fun p -> p.picture ) post
// the operator I am looking should be a shortcut for the previous operations:
let maybeImage = post?picture
Upvotes: 0
Views: 513
Reputation: 5751
Record fields are usually accessed by compiler-generated properties, where the field name is the same as the property name. Alternatively, you might use Record Patterns
, see spec 7.10, to match the field name and retrieve the field's value.
Also, in monad terms, the operation you are after is map
, not bind
, as Bent's answer points out.
type Post = { picture: string }
let post = None
let maybeImage = Option.map (fun { picture = p } -> p)
// val maybeImage : (Post option -> string option)
maybeImage post
// val it : string option = None
While bind
has a canonical operator >>=
, map
is often considered a functor. As such, you can replace it with functional composition >> map
, here >> Option.map
.
Generally, it is not recommended, but by using Reflection it's actually quite possible and in some situations even very useful to define a custom operator that combines the record accessor and the map
operation. It's a generic operator that takes the field name as a string and would work on any record, but must be given enough type information being able to figure out the result type.
open Microsoft.FSharp.Reflection
let (?) (o : 'T option) name : 'R option =
typeof<'T>.GetProperty name |> function
| null -> None
| pi -> Option.map (pi.GetValue >> unbox<'R>) o
// val ( ? ) : o:'T option -> name:string -> 'R option
let pictureName : string option = post?picture
// val pictureName : string option = None
Upvotes: 2
Reputation: 3470
type Post = { picture: string }
let getPostPicture (post: Post option) =
Option.map (fun p -> p.picture) post
let post = Some { picture = "the picture" }
let picture = getPostPicture post
match picture with
| Some picture -> printfn "%s" picture
| None -> printfn "No picture."
You should not use Value, and not test for option values the way you did. Instead,
let maybeImage = match post with Some post -> Some post.picture | None -> None
Which can be shortened to
let maybeImage = Option.map (fun p -> p.picture) post
Upvotes: 0