Result bind with different types? - f#

I've made a simple Computation Expression workflow for dealing with Results.
[<RequireQualifiedAccess>]
module Result =
type Builder() =
member __.Bind(x, f) = x |> Result.bind f
member __.Return(x) = x
member __.ReturnFrom(x) = Ok x
let workflow = Builder()
I also use different types to represent different kids of errors:
type ValidationError<'a> = { Obj:'a; Message:string }
type InvalidOperationError = { Operation:string; Message:string }
The problem arises when two results have different error types.
LetterString.create : string -> Result<LetterString, ValidationError<string>>
Username.create : string -> Result<Username, ValidationError<string>>
PositiveDecimal.create : decimal -> Result<PositiveDecimal, ValidationError<decimal>>
let user =
Result.workflow {
let! name = LetterString.create "Tom"
let! username = Username.create "Tom01098"
// Error occurs here.
let! balance = PositiveDecimal.create 100m
return! {
// User record creation elided.
}
}
FS0001 Type mismatch. Expecting a
'Result<PositiveDecimal,ValidationError<string>>'
but given a
'Result<PositiveDecimal,ValidationError<decimal>>'
I have already tried using a DU type of all errors:
type Error<'a> =
| ValidationError of Obj:'a * Message:string
| InvalidOperationError of Operation:string * Message:string
This has a similar problem when the generic parameter 'a is different between errors. It also loses the exact type of error in the type signature of the function.
The expected result is that the entire workflow has a unified error type, preferably as specific as possible in terms of type.

Can be solved by removing the generic parameter and using a single Error DU. Unfortunately this loses the signature I wanted, but it will have to do.

Related

Casting a system.Object to a specific type in F#

I have a standard type that I use to pass messages and objects between functions that has an optional MessageObject that is System.Object.
type StatusMessage = {
Message: string
MessageObject: Object option
Success: bool
}
In the following specific function - I know that the Message Object will always contain the following specific type and I want to be able to access that within the function
type SingleCorrectRecord = {
CardTransactionWithOrder: CardWithOrder
Line: RowTransaction
ExchangeVariance: decimal
}
My function is:
let getEstimatedVariance(matchedTransactions: StatusMessage list): decimal =
let exchangeVariance:decimal =
matchedTransactions |> Seq.sumBy(fun mt -> mt.MessageObject.ExchangeVariance)
estimatedVariance
Obviously mt.MessageObject doesn't contain the property "ExchangeVariance" but I need to be able to cast (or unbox?) the object so that it knows that it is a SingleCorrectRecord type and I can access the properties.
Any help is appreciated.
You can nest the dynamic type test with the union case pattern in the same match expression:
let exchangeVariance =
matchedTransactions |> Seq.sumBy (fun mt ->
match mt.MessageObject with
| Some(:? SingleCorrectRecord as scr) -> scr.ExchangeVariance
| _ -> 0M )
// val exchangeVariance : decimal
:?> is the downcast operator (or :? within pattern matching) which can be used for this problem. However, it is typically not recommended to use downcasting, which can fail at runtime, in F# code.
As an alternative, you could structure your StatusMessage type to be generic or to use a discriminated union depending on whether messages with different payload types need to be stored in the same collection.
// This type can store any type of message however StatusMessage<SingleCorrectRecord> is
// a different type to StatusMessage<Other> and they can't be stored in the same collection.
type StatusMessage<'T> = {
Message: string
MessageObject: 'T option
Success: bool
}
let getEstimatedVariance(matchedTransactions: StatusMessage<SingleCorrectRecord> list): decimal =
let estimatedVariance:decimal =
matchedTransactions |> Seq.sumBy(fun mt ->
match mt.MessageObject with
| Some msg -> msg.ExchangeVariance
| None -> 0M )
estimatedVariance
If the messages need to be in a grouped collection you could define all messages in a MsgPayload discriminated union:
type MsgPayload =
| SingleCorrectRecord of SingleCorrectRecord
| Other of string
type StatusMessage2 = {
Message: string
MessageObject: MsgPayload option
Success: bool
}
let getEstimatedVariance2(matchedTransactions: StatusMessage2 list): decimal =
let estimatedVariance:decimal =
matchedTransactions |> Seq.sumBy(fun mt ->
match mt.MessageObject with
| Some (SingleCorrectRecord msg) -> msg.ExchangeVariance
| _ -> 0M )
estimatedVariance

Trying to validate CSV data in f#

Just trying to wrap my head around some F# here and I'm having an issue.
I have a CSV file which looks like
CorrelationId,TagNumber,Description,CreationDate,UpdateDate,Discipline
8D3F96F3-938F-4599-BCA1-66B13199A39A,Test 70-2,Test tag - Ignore,2016-04-05 14:55:23.503,2016-04-05 14:55:23.503,Mechanical
A9FD4B9D-F7A1-4B7D-917F-D633EA0321E3,test-4,A test tag 24,2016-03-23 15:09:54.667,2016-03-30 17:35:29.553,Civil
And I'm reading it in using the CSV type provider
open FSharp.Data
type Tag = CsvProvider<"tags.csv">
let readTags (path:string) =
let tags = Tag.Load(path)
printfn "%i" ( tags.Rows |> Seq.length )
let tag = tags.Rows |> Seq.head
Then I'd like to validate the rows so I took a hint from the fsharpforfunandprofit railway oriented programming.
type Result<'TSuccess,'TFailure> =
| Success of 'TSuccess
| Failure of 'TFailure
let bind switchFunction twoTrackInput =
match twoTrackInput with
| Success s -> switchFunction s
| Failure f -> Failure f
let validateTagName tag =
if String.length tag.TagNumber = 0 then Failure "Tag number cannot be empty"
else Success tag
let validateTagDescription tag =
if String.length tag.Description = 0 then Failure "Tag description cannot be empty"
else Success tag
But I'm getting a problem in the validation methods that I need to annotate the functions with a type. I have no idea what type to annotate these as. I tried playing with creating a new type and mapping to it
type tagType = { TagNumber: string; Description: string}
which made those functions compile properly but I just kicked the problem down the road because now I'm not sure how to map from the Tag.Row to tagType. Ideally I'd do this validation without having to do any mapping.
How should all this look?
You already have the Tag type from the type provider. With that particular data sample, it provides a nested type called Tag.Row. You can annotate your functions with that type:
let validateTagName (tag : Tag.Row) =
if String.length tag.TagNumber = 0 then Failure "Tag number cannot be empty"
else Success tag
let validateTagDescription (tag : Tag.Row) =
if String.length tag.Description = 0 then Failure "Tag description cannot be empty"
else Success tag
These functions compile.
To add to Mark's answer, the problem is that dotting into a class in the OOP way generally needs a type annotation,
because the type inference can't normally tell what type is being used.
For example, what is the type of x here?
let doSomething x = x.Length // error FS0072: Lookup on object of indeterminate type
Using a function attached to a module would give the type inference the information it needs:
let doSomething x = List.length x // Compiles OK
The type inference will normally work with record types that you have defined:
type tagType = { TagNumber: string; Description: string}
let doSomething x = x.TagNumber // Compiles OK
But in this case you are working with a class defined by a type provider, so the type inference is not working as well.
As Mark says, the easiest thing to do is to use a type annotation, in the way that he demonstrates.
The alternative would be to write a converter function from the type provider Tag type to your own MyTag type, and then do
let myTags = tags.Rows |> Seq.map convertToMyTag
to convert each row into your type. I sometimes do that when I want a more sophisticated domain type than just a simple record with fields.
In this scenario, though, that would be overkill (and you'd still need to add an annotation to the converter function!)
Finally, here are two posts that might be useful: understanding type inference
and troubleshooting common compiler errors.

How do I retrieve a value from a composite generic type?

How do I retrieve a value from a generic?
Specifically, I am attempting the following:
// Test
let result = Validate goodInput;;
// How to access record??
let request = getRequest result
Here's the code:
type Result<'TSuccess,'TFailure> =
| Success of 'TSuccess
| Failure of 'TFailure
let bind nextFunction lastFunctionResult =
match lastFunctionResult with
| Success input -> nextFunction input
| Failure f -> Failure f
type Request = {name:string; email:string}
let validate1 input =
if input.name = "" then Failure "Name must not be blank"
else Success input
let validate2 input =
if input.name.Length > 50 then Failure "Name must not be longer than 50 chars"
else Success input
let validate3 input =
if input.email = "" then Failure "Email must not be blank"
else Success input;;
let Validate =
validate1
>> bind validate2
>> bind validate3;;
// Setup
let goodInput = {name="Alice"; email="abc#abc.com"}
let badInput = {name=""; email="abc#abc.com"};;
// I have no clue how to do this...
let getRequest = function
| "Alice", "abc#abc.com" -> {name="Scott"; email="xyz#xyz.com"}
| _ -> {name="none"; email="none"}
// Test
let result = Validate goodInput;;
// How to access record??
let request = getRequest result
printfn "%A" result
You mean how do you extract the record out of your result type? Through pattern matching, that's what you're already doing in bind.
let getRequest result =
match result with
| Success input -> input
| Failure msg -> failwithf "Invalid input: %s" msg
let result = Validate goodInput
let record = getRequest result
This will return the record or throw an exception. Up to you how you handle the success and failure cases once you have your Result - that could be throwing an exception, or turning it into option, or logging the message and returning a default etc.
This seems to be a frequently asked question: How do I get the value out of a monadic value? The correct answer, I believe, is Mu.
The monadic value is the value.
It's like asking, how do I get the value out of a list of integers, like [1;3;3;7]?
You don't; the list is the value.
Perhaps, then, you'd argue that lists aren't Discriminated Unions; they have no mutually exclusive cases, like the above Result<'TSuccess,'TFailure>. Consider, instead, a tree:
type Tree<'a> = Node of Tree<'a> list | Leaf of 'a
This is another Discriminated Union. Examples include:
let t1 = Leaf 42
let t2 = Node [Node []; Node[Leaf 1; Leaf 3]; Node[Leaf 3; Leaf 7]]
How do you get the value out of a tree? You don't; the tree is the value.
Like 'a option in F#, the above Result<'TSuccess,'TFailure> type (really, it's the Either monad) is deceptive, because it seems like there should only be one value: the success. The failure we don't like to think about (just like we don't like to think about None).
The type, however, doesn't work like that. The failure case is just as important as the success case. The Either monad is often used to model error handling, and the entire point of it is to have a type-safe way to deal with errors, instead of exceptions, which are nothing more than specialised, non-deterministic GOTO blocks.
This is the reason the Result<'TSuccess,'TFailure> type comes with bind, map, and lots of other goodies.
A monadic type is what Scott Wlaschin calls an 'elevated world'. While you work with the type, you're not supposed to pull data out of that world. Rather, you're supposed to elevate data and functions up to that world.
Going back to the above code, imagine that given a valid Request value, you'd like to send an email to that address. Therefore, you write the following (impure) function:
let send { name = name; email = email } =
// Send email using name and email
This function has the type Request -> unit. Notice that it's not elevated into the Either world. Still, you want to send the email if the request was valid, so you elevate the send method up to the Either world:
let map f = bind (fun x -> Success (f x))
let run = validate1 >> bind validate2 >> bind validate3 >> map send
The run function has the type Request -> Result<unit,string>, so used with goodInput and badInput, the results are the following:
> run goodInput;;
val it : Result<unit,string> = Success unit
> run badInput;;
val it : Result<unit,string> = Failure "Name must not be blank"
And then you probably ask: and how do I get the value out of that?
The answer to that question depends entirely on what you want to do with the value, but, imagine that you want to report the result of run back to the user. Displaying something to the user often involves some text, and you can easily convert a result to a string:
let reportOnRun = function
| Success () -> "Email was sent."
| Failure msg -> msg
This function has the type Result<unit,string> -> string, so you can use it to report on any result:
> run goodInput |> reportOnRun;;
val it : string = "Email was sent."
> run badInput |> reportOnRun;;
val it : string = "Name must not be blank"
In all cases, you get back a string that you can display to the user.

Is it possible to create a record type whose field is a function that takes a generic parameter, without specifying the type when creating the record?

I want to define the following record type:
type LogFuncs = {
LogWithLabel : string -> 'a -> unit
LogWithLabelAndReturn : string -> 'a -> 'a }
The intention is that I can define one function of string -> unit and then use that to derive several convenience functions, like so:
let makeLogFuncs outputFunc =
let logWithLabelFunc label value = sprintf "%s: %A" label value |> outputFunc
let logWithLabelAndReturnFunc label value = logWithLabelFunc label value; value
{ LogWithLabel = logWithLabelFunc
LogWithLabelAndReturn = logWithLabelAndReturnFunc }
But, the compiler won't let me do this without specifying an <'a> when making an instance of type LogFuncs, and that's not what I want to do -- I want to be able to call this function on any 'a. (I will also want to provide related functions, hence the use of the record type.)
Is it possible to define a record type with a field containing type parameter that is not also a type parameter of the record type itself?
I don't believe it is possible to do with record types. But I can define a class which provides the mechanism I wanted:
type Logger (outputFunc: string->unit) =
member __.LogWithLabel label value = sprintf "%s: %A" label value |> outputFunc
member x.LogWithLabelAndReturn label value = x.LogWithLabel label value; value
Then I can do:
let log = new Loggery (printfn "%s")
let ``result should equal 5`` = 5 |> log.LogWithLabelAndReturn "Beans"
...and it correctly prints "Beans: 5" and returns 5.
You can make the record itself generic:
type LogFuncs<'a> = {
LogWithLabel : string -> 'a -> unit
LogWithLabelAndReturn : string -> 'a -> 'a }
That also makes the makeLogFuncs generic. It's still usable, but probably not in the way you want:
(makeLogFuncs (printfn "%s")).LogWithLabel "Number" 42
(makeLogFuncs (printfn "%s")).LogWithLabelAndReturn "Number" 42
(makeLogFuncs (printfn "%s")).LogWithLabel "Text" "Foo"
(makeLogFuncs (printfn "%s")).LogWithLabelAndReturn "Text" "Foo"
As the answer provided by Overlord Zurg implies, the OP approach is quite object-oriented, so use objects if you want to design the system in that way.

Why doesn't F# infer the record type when updating a record?

This example code:
type recordA = { X: string; }
type recordB = { X: string; }
let modifyX newX record = { record with X = newX }
let modifiedRecordA = {recordA.X = "X"} |> modifyX "X2"
let modifiedRecordB = {recordB.X = "X"} |> modifyX "X2"
Results in:
let modifyX newX record = { record with X = newX }
--------------------------^^^^^^^^^^^^^^^^^^^^^^^^
stdin(4,27): warning FS0667: The field labels and expected type of this record expression or pattern do not uniquely determine a corresponding record type
let modifiedRecordA = {recordA.X = "X"} |> modifyX "X2"
-------------------------------------------^^^^^^^^^^^^
stdin(6,44): error FS0001: Type mismatch. Expecting a
recordA -> 'a
but given a
recordB -> recordB
The type 'recordA' does not match the type 'recordB'
My expectation is that modifiedRecordA ends up equivalent to { recordA.X = "X2" } and modifiedRecordB ends up equivalent to { recordB.X = "X2" }, but it doesn't seem to work that way.
Why doesn't this just infer and return the appropriate record type based on the parameter type?
Is there anything I can do to make this work?
The inline magic required to make this work is based on overload resolution combined with statically resolved member constraints. Overloads defined as operators avoid the need to spell out explicit constraints.
type Foo = Foo with
static member ($) (Foo, record) = fun newX -> { record with recordA.X = newX }
static member ($) (Foo, record) = fun newX -> { record with recordB.X = newX }
let inline modifyX newX record = (Foo $ record) newX
let modifiedRecordA = {recordA.X = "X"} |> modifyX "X2"
let modifiedRecordB = {recordB.X = "X"} |> modifyX "X2"
Constructs which pass types for which no overload exists do not compile.
type recordC = { X: string; }
let modifiedRecordC = {recordC.X = "X"} |> modifyX "X2"
// Error No overloads match for method 'op_Dollar' ...
// Possible overload ... The type 'recordC' is not compatible with the type 'recordB'
// Possible overload ... The type 'recordC' is not compatible with the type 'recordA'
This is not really intended for actual use. Heed the advise and explore if other approaches are better suited to your problem.
the function modifyX is incorrect. You may not use the term X in the definition and have that X refer to different fields.
Section 6.3.6 of the F# spec
Each field label must resolve to a field Fi in a single record type R, all of whose fields are accessible.
By passing recordA and recordB to modifyX the X is not uniquely determined to be a field of a single type.
What you actually want is probably a polymorphic property member inherited through an interface, not a set of record types with a common member name.
The problem is that the compiler is inferring the type of modifyX based on usage. My understanding is that this proceeds from the bottom up, so the type is inferred to be val modifyX : newX:string -> record:recordB -> recordB. This, of course, then results in a type error when attempting to use this with a record of type recordA. The warning is telling you that, although its picking a type, there is another type with the same fields, so all the compiler can do is make its best guess at which type you meant. It might be possible to achieve what you're trying to do with inline functions, but I'm not sure how that might work offhand.

Resources