How to use bind and map in place of nested matches - f#

F# 6.0.3
I have seen some solutions on Google that are close to what I need; but being a Newbie I can't quite get how to use bind and map to get the solution.
I have many working procedures of the following format:
Example #1:
let saveAllDiagnosis =
let savealldiagnosis = match m.Encounter with
| None -> failwith "No encounter found"
| Some e -> match e.EncounterId with
| None -> failwith "No Encounter id found"
| Some id -> m.AllDiagnosisList
|> List.iter ( fun dx -> match dx.Key with
| None -> ()
| Some k -> Async.RunSynchronously (editAllDiagnosisInPreviousEncountersAsync id dx))
savealldiagnosis
Example #2
let saveEncounterDiagnosis =
let savedx = match m.Encounter with
| None -> failwith "No encounter found"
| Some e -> match e.EncounterId with
| None -> failwith "No Encounter id found"
| Some id -> m.BillingDiagnosisList |> List.iter ( fun dx -> Async.RunSynchronously (saveDxAsync id dx))
savedx
As can be seen, these are nested methods with almost identical behavior--differing only in the async procedure being called and the initializing list. What I would like to do is something along the lines of:
let runProcedures (fn: Model->Async) Model = ????
That is, a single procedue that encapsulates everything except the Async method and it's parameters but manages all the "None"s in a better way.
I hope my intent is clear.
TIA

If you are happy with using exceptions, then you do not even need railway-oriented programming (ROP). ROP is useful for more complex validation tasks, but I think exceptions are often perfectly reasonable and easy way of handling errors. In your case, you could define a helper that extracts a value of option<'T> or fails with a given error message:
let orFailWith msg opt =
match opt with
| Some v -> v
| None -> failwithf "%s" msg
Using this, you can then rewrite your code as follows:
let saveAllDiagnosis =
let e = m.Encounter |> orFailWith "No encounter found"
let id = e.EncounterId |> orFailWith "No Encounter id found"
for dx in m.AllDiagnosisList do
dx.Key |> Option.iter (fun k ->
editAllDiagnosisInPreviousEncountersAsync id dx |> Async.RunSynchronously)
let saveEncounterDiagnosis =
let e = m.Encounter |> orFailWith "No encounter found"
let id = e.EncounterId |> orFailWith "No Encounter id found"
for dx in m.BillingDiagnosisList do
saveDxAsync id dx |> Async.RunSynchronously
As I do not know the broader context of this, it is hard to say more - your code is imperative, but that may be perfectly fine if you are following the sandwich pattern.

Using mentioned ROP code can be rewritten as such. Result is used to track error and throw it at the end of pipeline. With current design is possible to avoid exceptions by just logging error instead of throwing at before last line.
type Encounter = { EncounterId : int option }
type Diagnostic = { Key : int option }
type Thing = {
Encounter : Encounter option
AllDiagnosisList : Diagnostic list
}
let editAllDiagnosisInPreviousEncountersAsync id diag = async { return () }
module Result =
let ofOption err opt =
match opt with
| Some v -> Ok v
| None -> Error err
let join res =
match res with
| Error v
| Ok v -> v
let saveAllDiagnosis m =
m.Encounter
|> Result.ofOption "No encounter found" // get value from option or log error
|> Result.map (fun e -> e.EncounterId)
|> Result.bind (Result.ofOption "No Encounter id found") // get EncounterId or log error
|> Result.map (fun id -> (
m.AllDiagnosisList
|> Seq.where (fun dx -> dx.Key.IsSome)
|> Seq.iter (fun dx -> Async.RunSynchronously (editAllDiagnosisInPreviousEncountersAsync id dx))
))
|> Result.mapError failwith // throw error
|> Result.join // Convert Result<unit, unit> into unit

The solutions posted above are very helpful to this newbie. But adding my own two cents worth, I going with this:
let _deleteDxFromEncounterAsync (encounterId:int) (dx:Diagnosis) : Async<unit> = deleteDxFromEncounterAsync encounterId dx.Description
let _deleteDxFromAllPreviousEncountersAsync (encounterId:int) (dx:Diagnosis) : Async<unit> = deleteDxFromAllPreviousEncountersAsync encounterId dx.Description
let _saveDxAsync (encounterId:int) (dx:Diagnosis) : Async<unit> = saveDxAsync encounterId dx
let _editAllDiagnosisInPreviousEncountersAsync (encounterId:int) (dx:Diagnosis) : Async<unit> = editAllDiagnosisInPreviousEncountersAsync encounterId dx
let listchk (dxs:Diagnosis list) : Diagnosis list option =
match dxs with
| [] -> None
| _ -> Some dxs
let _save (fn:int -> Diagnosis-> Async<unit>) (dxs:Diagnosis list) : unit =
match dxs |> listchk, m.Encounter |> Option.bind (fun v -> v.EncounterId) with
| Some dxs, Some id -> dxs |> List.iter (fun dx -> Async.RunSynchronously(fn id dx))
| _,_ -> failwith "Missing Encounter or EncounterId or Empty List"
m.DeletedBillingDiagnosis |>_save _deleteDxFromEncounterAsync
m.DeletedAllDiagnosis |>_save _deleteDxFromAllPreviousEncountersAsync
m.BillingDiagnosisList |>_save _saveDxAsync
m.AllDiagnosisList |> List.filter (fun dx -> dx.Key.IsSome) |>_save _editAllDiagnosisInPreviousEncountersAsync
For speed, in the future, I will probably have the Async functions act on the entire list at one time rather then one item; but for now, this code comes closest to my intent in asking the question. IMPROVEMENTS AND CRITISM IS GLADDLY APPRECIATED! F# is fun!
Thanks to all.

Related

Can I make return type vary with parameter a bit like sprintf in F#?

In the F# core libraries there are functions whose signature seemingly changes based on the parameter at compile-time:
> sprintf "Hello %i" ;;
val it : (int -> string) = <fun:it#1>
> sprintf "Hello %s" ;;
val it : (string -> string) = <fun:it#2-1>
Is it possible to implement my own functions that have this property?
For example, could I design a function that matches strings with variable components:
matchPath "/products/:string/:string" (fun (category : string) (sku : string) -> ())
matchPath "/tickets/:int" (fun (id : int) -> ())
Ideally, I would like to do avoid dynamic casts.
There are two relevant F# features that make it possible to do something like this.
Printf format strings. The compiler handles format strings like "hi %s" in a special way. They are not limited just to printf and it's possible to use those in your library in a somewhat different way. This does not let you change the syntax, but if you were happy to specify your paths using e.g. "/products/%s/%d", then you could use this. The Giraffe library defines routef function, which uses this trick for request routing:
let webApp =
choose [
routef "/foo/%s/%s/%i" fooHandler
routef "/bar/%O" (fun guid -> text (guid.ToString()))
]
Type providers. Another option is to use F# type providers. With parameterized type providers, you can write a type that is parameterized by a literal string and has members with types that are generated by some F# code you write based on the literal string parameter. An example is the Regex type provider:
type TempRegex = Regex< #"^(?<Temperature>[\d\.]+)\s*°C$", noMethodPrefix = true >
TempRegex().Match("21.3°C").Temperature.TryValue
Here, the regular expression on the first line is static parameter of the Regex type provider. The type provider generates a Match method which returns an object with properties like Temperature that are based on the literal string. You would likely be able to use this and write something like:
MatchPath<"/products/:category/:sku">.Match(fun r ->
printfn "Got category %s and sku %s" r.Category r.Sku)
I tweaked your example so that r is an object with properties that have names matching to those in the string, but you could use a lambda with multiple parameters too. Although, if you wanted to specify types of those matches, you might need a fancier syntax like "/product/[category:int]/[sku:string]" - this is just a string you have to parse in the type provider, so it's completely up to you.
1st: Tomas's answer is the right answer.
But ... I had the same question.
And while I could understand it conceptually as "it has to be 'the string format thing' or 'the provider stuff'"
I could not tell my self that I got until I tried an implementation
... And it took me a bit .
I used FSharp.Core's printfs and Giraffe's FormatExpressions.fs as guidelines
And came up with this naive gist/implementation, inspired by Giraffe FormatExpressions.fs
BTW The trick is in this bit of magic fun (format: PrintfFormat<_, _, _, _, 'T>) (handle: 'T -> 'R)
open System.Text.RegularExpressions
// convert format pattern to Regex Pattern
let rec toRegexPattern =
function
| '%' :: c :: tail ->
match c with
| 'i' ->
let x, rest = toRegexPattern tail
"(\d+)" + x, rest
| 's' ->
let x, rest = toRegexPattern tail
"(\w+)" + x, rest
| x ->
failwithf "'%%%c' is Not Implemented\n" x
| c :: tail ->
let x, rest = toRegexPattern tail
let r = c.ToString() |> Regex.Escape
r + x, rest
| [] -> "", []
// Handler Factory
let inline Handler (format: PrintfFormat<_, _, _, _, 'T>) (handle: 'T -> string) (decode: string list -> 'T) =
format.Value.ToCharArray()
|> List.ofArray
|> toRegexPattern
|> fst, handle, decode
// Active Patterns
let (|RegexMatch|_|) pattern input =
let m = Regex.Match(input, pattern)
if m.Success then
let values =
[ for g in Regex(pattern).Match(input).Groups do
if g.Success && g.Name <> "0" then yield g.Value ]
Some values
else
None
let getPattern (pattern, _, _) = pattern
let gethandler (_, handle, _) = handle
let getDecoder (_, _, decode) = decode
let Router path =
let route1 =
Handler "/xyz/%s/%i"
(fun (category, id) ->
// process request
sprintf "handled: route1: %s/%i" category id)
(fun values ->
// convert matches
values |> List.item 0,
values
|> List.item 1
|> int32)
let route2 =
Handler "/xyz/%i"
(fun (id) -> sprintf "handled: route2: id: %i" id) // handle
(fun values -> values|> List.head |> int32) // decode
// Router
(match path with
| RegexMatch (getPattern route2) values ->
values
|> getDecoder route2
|> gethandler route2
| RegexMatch (getPattern route1) values ->
values
|> getDecoder route1
|> gethandler route1
| _ -> failwith "No Match")
|> printf "routed: %A\n"
let main argv =
try
let arg = argv |> Array.skip 1 |> Array.head
Router arg
0 // return an integer exit code
with
| Failure msg ->
eprintf "Error: %s\n" msg
-1

F# Computation Expression to build state and defer execution

I am looking to build a computation expression where I can express the following:
let x = someComputationExpression {
do! "Message 1"
printfn "something 1"
do! "Message 2"
printfn "something 2"
do! "Message 3"
printfn "something 3"
let lastValue = 4
do! "Message 4"
// need to reference values across `do!`
printfn "something %s" lastValue
}
and be able to take from x a list:
[| "Message 1"
"Message 2"
"Message 3"
"Message 4" |]
without printfn ever getting called, but with the ability to later execute it (if that makes sense).
It doesn't need to be with the do! keyword, it could be yield or return, whatever is required for it to work.
To put it another way, I want to be able to collect some state in a computation express, and queue up work (the printfns) that can be executed later.
I have tried a few things, but am not sure it's possible.
It's a bit hard to figure out a precise solution from the OP question. Instead I am going to post some code that the OP perhaps can adjust to the needs.
I define Result and ResultGenerator
type Result =
| Direct of string
| Delayed of (unit -> unit)
type ResultGenerator<'T> = G of (Result list -> 'T*Result list )
The generator produces a value and a list of direct and delayed values, the direct values are the string list above but intermingled with them are the delayed values. I like returning intermingled so that the ordering is preserved.
Note this is a version of what is sometimes called a State monad.
Apart from the class CE components like bind and Builders I created two functions direct and delayed.
direct is used to create a direct value and delayed a delayed one (takes a function)
let direct v : ResultGenerator<_> =
G <| fun rs ->
(), Direct v::rs
let delayed d : ResultGenerator<_> =
G <| fun rs ->
(), Delayed d::rs
To improve the readability I defined delayed trace functions:
let trace m : ResultGenerator<_> =
G <| fun rs ->
(), Delayed (fun () -> printfn "%s" m)::rs
let tracef fmt = kprintf trace fmt
From an example generator:
let test =
builder {
do! direct "Hello"
do! tracef "A trace:%s" "!"
do! direct "There"
return 123
}
The following result was achieved:
(123, [Direct "Hello"; Delayed <fun:trace#37-1>; Direct "There"])
(Delayed will print the trace when executed).
Hope this can give some ideas on how to attack the actual problem.
Full source:
open FStharp.Core.Printf
type Result =
| Direct of string
| Delayed of (unit -> unit)
type ResultGenerator<'T> = G of (Result list -> 'T*Result list )
let value v : ResultGenerator<_> =
G <| fun rs ->
v, rs
let bind (G t) uf : ResultGenerator<_> =
G <| fun rs ->
let tv, trs = t rs
let (G u) = uf tv
u trs
let combine (G t) (G u) : ResultGenerator<_> =
G <| fun rs ->
let _, trs = t rs
u trs
let direct v : ResultGenerator<_> =
G <| fun rs ->
(), Direct v::rs
let delayed d : ResultGenerator<_> =
G <| fun rs ->
(), Delayed d::rs
let trace m : ResultGenerator<_> =
G <| fun rs ->
(), Delayed (fun () -> printfn "%s" m)::rs
let tracef fmt = kprintf trace fmt
type Builder() =
class
member x.Bind (t, uf) = bind t uf
member x.Combine (t, u) = combine t u
member x.Return v = value v
member x.ReturnFrom t = t : ResultGenerator<_>
end
let run (G t) =
let v, rs = t []
v, List.rev rs
let builder = Builder ()
let test =
builder {
do! direct "Hello"
do! tracef "A trace:%s" "!"
do! direct "There"
return 123
}
[<EntryPoint>]
let main argv =
run test |> printfn "%A"
0

Parameterized exception type [duplicate]

How can I define an exception like the following?
exception CustomExn<'TMessage> of 'TMessage list
Maybe you can just inherit from System.Exception?
type CustomExn<'TMessage> (message:'TMessage list) =
inherit System.Exception ()
let test =
try
raise (CustomExn["string"] )
with
| :? CustomExn<string> -> "CustomExn of string"
| :? CustomExn<int> -> "CustomExn of int"
| _ -> "Everything else"
Not sure it is possible with F# Exception Definitions according to specs (page 164-165)
This one also NOT A GOOD SOLUTION because try with will only catch ExceptionList<string> in this case so there is no good way to make it generic
type ExceptionList<'a>(msgList: 'a list) =
inherit Exception()
member __.MessageList = msgList
let mock() =
raise <| ExceptionList(["failed"])
try
mock() //raises ExceptionList<string>
with
//this catch block won't fire, because it is not generic, it is ExceptionList<obj>
| :? ExceptionList<_> as exnList ->
exnList.MessageList
|> List.iter (printfn "%A")
The better way though: Result<'a,'b list>:
let mock'() =
if DateTime.Now.Ticks % 2L = 0L
then Ok()
else Error(["failed"])
let result = mock'()
match result with
| Ok _ -> printfn "Ok!"
| Error (msgList) ->
msgList
|> List.iter (printfn "%A")
Added There is a workaround with type loss:
type ExceptionList(msgList: obj list) =
inherit Exception()
member __.MessageList = msgList
// Here is F# exception definition
exception CustomException of ExceptionList
let failwithMany msgs =
raise <| CustomException (ExceptionList(msgs))
let mock() =
//zero type checking here
failwithMany[1;2;"a";[];"failed"]
try
mock()
with
// exnList is list of objects
| CustomException exnList ->
exnList.MessageList
|> List.iter (printfn "%A")

F# Compiler Service: get a list of names visible in the scope

How can I get get a list of names visible in the scope with FSC?
I tried this:
#r "../../packages/FSharp.Compiler.Service.16.0.2/lib/net45/FSharp.Compiler.Service.dll"
open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.SourceCodeServices
do
let file = "TestFileName.fsx"
let checker = SourceCodeServices.FSharpChecker.Create()
let code =
"""
let testStr = "x"
t
"""
async{
let! options, _ = checker.GetProjectOptionsFromScript(file,code)
let! parseRes,checkAnser = checker.ParseAndCheckFileInProject(file, 0, code, options)
match checkAnser with
| FSharpCheckFileAnswer.Succeeded checkRes ->
let! decls =
checkRes.GetDeclarationListInfo(
Some parseRes, //ParsedFileResultsOpt
3 , //line
1 , //colAtEndOfPartialName
"t" , //lineText
[ "t" ] , //qualifyingNames
"" , //partialName
( fun _ -> [] ) //getAllSymbols: (unit -> AssemblySymbol list)
)
if Seq.isEmpty decls.Items then
printfn "*no declarations found*"
else
decls.Items
|> Seq.sortBy (fun d -> d.Name)
|> Seq.truncate 10
|> Seq.iter (fun d -> printfn "decl: %s" d.Name)
| _ -> failwithf "*Parsing did not finish... "
} |> Async.RunSynchronously
but it only prints "no declarations found". I would expect not only testStr but also all the other names that are available by default.
I did not find an example in the documentation.
qualifyingNames should be an empty list, it’s for dot separated prefix, excluding the last (possibly partial) ident. However, there is no a method in FCS that returns unfiltered list of names for scope, yet it’s really easy to add one.
With the answer of vasily-kirichenko and using the current FCS 17.0.1 I came up with this solution:
#r "../../packages/FSharp.Compiler.Service.17.0.1/lib/net45/FSharp.Compiler.Service.dll"
open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.SourceCodeServices
do
let file = "TestFileName.fsx"
let checker = SourceCodeServices.FSharpChecker.Create()
let code =
"""
let testStr = "x"
testStr.
"""
async{
let! options, _ = checker.GetProjectOptionsFromScript(file,code)
let! parseRes,checkAnser = checker.ParseAndCheckFileInProject(file, 0, code, options)
match checkAnser with
| FSharpCheckFileAnswer.Succeeded checkRes ->
let! decls =
let partialName = PartialLongName.Empty 6 //use any location before before the dot to get all declarations in scope
//let partialName = PartialLongName.Empty 7 //use the loacation of the dot (7) to get memebers of string
checkRes.GetDeclarationListInfo(
Some parseRes, // ParsedFileResultsOpt
3 , // line
"testStr." , // lineText
partialName, // PartialLongName
( fun _ -> [] ) // getAllSymbols: (unit -> AssemblySymbol list)
)
if Seq.isEmpty decls.Items then
printfn "*no declarations found*"
else
decls.Items
|> Seq.sortBy (fun d -> d.Name)
|> Seq.truncate 10
|> Seq.iter (fun d -> printfn "decl: %s" d.Name)
| _ -> failwithf "*Parsing did not finish... "
} |> Async.RunSynchronously

how can i use List.exists after a List.map without an extra let binding?

type ProcessAttachmentResult = ValidAttachment | InvalidAttachment
let processAttachment ( attachment : Attachment ) =
if attachment.Name ="test.txt" then
printfn "%s valid"
ValidAttachment
else
printfn "%s invalid" attachment.Name
InvalidAttachment
// attachments is of type List<Attachment>
let processedAttachments = attachments |> List.map processAttachment
// ProcessAttachmentResult list
let emailContainsValidAttachments =
List.exists ( fun r -> r = ValidAttachment) processedAttachments
match emailContainsValidAttachments with
| true -> move email toProcessedFolder
| _ -> move email toErrorFolder
How can i change the last two let bindings and match to a single binding?
i tried
attachments |> List.map processAttachment |> List.exists (fun r -> r = ValidAttachment)
but this gives:
This expression was expected to have type ProcessAttachmentResult list but here has type bool
As pad mentioned in a comment, there is nothing wrong with your approach. You must have accidentally redefined some built-in function (like List.exists). To check this, try opening a new F# Script File and paste the following code.
It is essentially your code with the missing declarations added and it type-checks just fine:
type ProcessAttachmentResult = ValidAttachment | InvalidAttachment
type Attachment = { Name : string }
let attachments = []
let move a b = ()
let email = 0
let toProcessedFolder = ""
let toErrorFolder = ""
let processAttachment ( attachment : Attachment ) =
if attachment.Name = "test.txt" then
printfn "%s valid" // TOMAS: Minor issue here - you missed the argument
ValidAttachment
else
printfn "%s invalid" attachment.Name
InvalidAttachment
// attachments is of type List<Attachment>
let processedAttachments = attachments |> List.map processAttachment
// ProcessAttachmentResult list
let emailContainsValidAttachments =
List.exists ( fun r -> r = ValidAttachment) processedAttachments
match emailContainsValidAttachments with
| true -> move email toProcessedFolder
| _ -> move email toErrorFolder
// TOMAS: No problem here - this type-checks without errors
attachments
|> List.map processAttachment
|> List.exists ( fun r -> r = ValidAttachment)
Seems like you need:
let emailContainsValidAttachments =
List.exists ( fun r -> r = ValidAttachment) (List.map attachments processAttachment)
The argument order, for some reason, is different in exists vs. map.

Resources