I need help understanding the concepts behind the following:
I have this:
type Result<'TSuccess,'TFailure> =
| Success of 'TSuccess
| Failure of 'TFailure
But this doesn't work:
let getRequest = function
| Success input -> input
| Failure msg -> msg
But this does:
let getRequest result =
match result with
| Success input -> input
| Failure msg -> failwithf "Invalid input: %s" msg
Why does the initial "getRequest" fail?
Again, I just don't understand the basic rules for pattern matching.
Can someone please shed some light on this?
The entire code is here:
module Core
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"};;
let getRequest = function
| Success input -> input
| Failure msg -> msg
// Test
let result = Validate goodInput;;
let request = getRequest result;;
Given this definition
let getRequest = function
| Success input -> input
| Failure msg -> msg
we can reason about its type as follows:
Since it's a function, it has type ?1 -> ?2 for some not-yet-known placeholder types.
The input uses the Success and Failure union cases, so the input must itself actually be some type Result<?3,?4>, and the function's type is Result<?3,?4> -> ?2.
The types of each branch must be the same as the function's return type.
Looking at the first branch, we see this means that ?3 = ?2.
Looking at the second branch, this means that ?4 = ?2.
Therefore the overall type of the function must be Result<?2,?2> -> ?2, or using real F# notation, Result<'a,'a> -> 'a where 'a can be any type - this is a generic function definition.
But Validate has type Request -> Result<Request, string>, so its output isn't consistent with this definition of getResult because the success and failure types are different and you can't just directly pass the results from one to the other.
On the other hand, with
let getRequest = function
| Success input -> input
| Failure msg -> failwithf "Invalid input: %s" msg
we would analyze it like this:
As before, any function has type ?1 -> ?2.
As before, the input must clearly be a Result<?3,?4>.
But analyzing the branches, we get a different result:
The first branch again leads to the constraint ?3 = ?2.
But the second branch is different - failwithf with that format pattern takes a string and throws an exception (and can have any return type, since it never returns normally), so we see msg must be a string, so ?4 = string.
So the overall type is Result<'a,string> -> 'a, which now allows you to pass a Result<Request,string> to it, as desired.
I think your question actually gives a good platform to explain pattern matching.
Let's start by thinking about the types involved, your getRequest function must be a function with some type signature getRequest : 'a -> 'b', i.e. it takes something of one type and returns something of another but let's look at what you've written.
let getRequest =
function
| Success input -> input // this branch would return 'TSuccess
| Failure msg -> msg // this brach would return 'TFailure
You can't have a function whose type signature is 'a -> 'b or 'c. (The only way to make this function compile is if 'b and 'c can be constrained to be the same type, then we do simply have a consistent 'a -> 'b).
But wait, enter the discriminated union.
Your discriminated union has type Result<'TSuccess,'TFailure>. Or, to describe things as I did above type 'd<'b,'c>. Now, we absolutely can have a function whose type signature is 'a -> 'd<'b,'c>.
'd<'b, 'c> is just a generic type which involves 'b and it involves 'c. The type signature includes all of these things, so hopefully you can see that we can contain both a 'TSuccess and 'TFailure in this type with no problem.
So, if we want to return something which can contain different combinations of types, we've found the perfect use case for the discriminated union. It can contain the 'TSuccess or the 'TFailure in one type. You can then choose between those results using pattern matching.
let getRequest =
function
| Success input -> printfn "%A" input // do something with success case
| Failure msg -> printfn "%A" msg // do something with failure case
So long as we have consistent return types in each case, we can insert any behaviour we want.
On to the next point of why throwing an exception is okay. Let's look at the type signature of the failwithf function: failwithf : StringFormat<'T,'Result> -> 'T. The failwithf function takes a string format and returns some type 'T.
So let's look at your second function again
let getRequest result =
match result with
| Success input -> input // type 'TSuccess
| Failure msg -> failwithf "Invalid input: %s" msg // type 'T inferred to be 'TSuccess
The function signature is consistent, 'a -> 'TSuccess.
Related
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
I am trying figure out if a generic type wrapped in a rop result is a list or not. This is what I tried but I got errors.
let checkType (result : RopResult<'tSuccess, 'errors>) =
match result with
| Success (s, msg) ->
match s with
| :? [] -> // error here
Sample
let isList<'s> () = true
let processList (ls : 'domain list) = true
let processType (s : 'domain) = true
let checkType (result : RopResult<'tSuccess, 'errors>) =
match result with
| Success (s, msg) ->
match s with
| s when isList<s>() -> processList s
| _ -> processType s
| Failure (x) -> false
I'll first explain the technicalities of how to get your code to work, and then try to convince you (as the other folks on this thread) that it may not be the right way to approach your problem.
Firstly, your match statement has a syntax error. You would write the type test and the cast in one swoop as
match s with
| :? List<int> as theIntList -> ...do something with theIntList ...
When you add that to your code, the F# compiler will complain "The runtime coercion or type test ... involves an indeterminate type. ... Further type annotations are needed". Fix that by being more specific about what kind of result your checkType is processing: it is some System.Object instance and the message, so you'd write:
let checkType (result : Result<obj*string, 'errors>) =
match result with
| Success (s, msg) ->
match s with
| :? List<int> as theIntList -> ... do something
Note that you can't change that to a generic thing like List<_> - F# will do the type test and the cast in one go, and would not hence know what to cast to. If you try to, you will see warnings that your List<_> has been inferred to be List<obj>
Having said all that: Using obj is not the idiomatic way to go, as others have tried to point out already. The answers of #robkuz and #TheInnerLight contain all you need: A map function, functions that operate on individual result types, which then becomes nicely composable:
let map f x =
match x with
| Success (s, msg) -> Success (f s, msg)
| Failure f -> Failure f
// This will automatically be inferred to be of type Result<(int list * string), 'a>
let myFirstResult = Success ([1;2], "I've created an int list")
// This will automatically be inferred to be of type Result<(string list * string), 'a>
let mySecondResult = Success (["foo"; "bar"], "Here's a string list")
// Process functions for specific result types. No type tests needed!
let processIntList (l: int list) = Seq.sum l
let processStringList = String.concat "; "
// This will automatically be inferred to be of type Result<(int * string), 'a>
let mapFirst = myFirstResult |> map processIntList
// This will automatically be inferred to be of type Result<(string * string), 'a>
let mapSecond = mySecondResult |> map processStringList
I am not sure if I really understand your problem.
In general if you have some polymorphic type (like your RopResult) and you want to process the polymorphic part of it a good approach in F# would be
to disentagle your code into a wrapper code and a processor code where your processor code is delivered via a higher order function for the processing part.
Example:
type RopResult<'tSuccess, 'tError> =
| Success of 'tSuccess
| Error of 'tError
let checkType (process: 'tSuccess -> 'tResult) (result : RopResult<'tSuccess, 'tError>) =
match result with
| Success s -> process s |> Success
| Error e -> Error e
and
let processList (ls : 'domain list) = true
let processType (s : 'domain) = true
and then you
checkType processList aListWrappedInResult
checkType processType aTypeWrappedInResult
Assuming you wanted to determine whether a supplied value was of a generic list type, you could do this:
let isList value =
let valueType = value.GetType()
match valueType.IsGenericType with
|true -> valueType.GetGenericTypeDefinition() = typedefof<_ list>
|false -> false
Example usage:
isList [5];;
val it : bool = true
isList ["a", "b"];;
val it : bool = true
isList "a";;
val it : bool = false
When working with something like RopResult, or more formally, Either, it's helpful to define the map function. The map function takes a function 'a -> 'b and gives you a function which operates in some elevated domain, e.g. RopResult<'a,'c> -> RopResult<'b,'c>.
This is analogous to List.map : ('a ->'b) -> 'a List -> 'b List.
We define it like this:
let map f v =
match v with
|Success sv -> Success (f sv)
|Failure fv -> Failure (fv)
You can then use isList on RopResults by simply doing:
ropResult |> map isList
Others here are warning you in the comments that there may be potential issues surrounding how you actually process the results once you've determined whether the type is a list or not. Specifically, you will need to ensure that the return types of your processList and processType functions are the same (although I would recommend revisiting the naming of processType and call it processValue instead. Since you are not operating on the type, I think the name is confusing).
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.
This is the code that I am working on:
// RopResult
type RopResult<'TSuccess, 'TMessage> =
| Success of 'TSuccess * 'TMessage list
| Failure of 'TMessage list
// result to return
type ResultForClient<'TSuccessObj, 'TMessages> =
bool * 'TSuccessObj * seq<'TMessages>
// return the above type
let resultToClient =
function
| Success (x, msg) -> true, x, Seq.ofList msg
| Failure errors -> false, Object(), Seq.ofList errors
The resultToClient function appears to just return a tuple opposed to the specific tuple type of ResultForClient. What am I missing here? On a side note, am I representing the RopResult type correctly in the return. As you can see the RopResult is generic so in case of failure I insert Object(). Does this seem correct?
Specify the return type:
let resultToClient v : ResultForClient<_,_> =
match v with
| Success (x, msg) -> true, x, Seq.ofList msg
| Failure errors -> false, Object(), Seq.ofList errors
It looks like you're attempting to do Railway Oriented Programming.
am I representing the RopResult type correctly in the return. As you can see the RopResult is generic so in case of failure I insert Object(). Does this seem correct?
No, that doesn't seem to be correct. By using Object(), the inferred type of resultToClient is
RopResult<System.Object,'a> -> ResultForClient<System.Object,'a>
Since ResultForClient is only a type alias, this is equivalent to
RopResult<Object,'a> -> bool * Object * seq<'a>
That is, the return value of the function is bool * Object * seq<'a>. This looks more like a nasty combination of return codes and untyped data than a Fucntional data type. Is that what you had in mind? What do you intend to do with the Object?
The point of Railway Oriented Programming is that the RopResult data type itself carries information about the result of any computation (whether or not it failed), so unless you're trying to interop with some other system that doesn't understand sum types (Discriminated Unions), you'll only be throwing away information by attempting to 'unwrap' the RopResult.
The following works as I would expect.
type Result =
| Success
| Failure
type Response =
| Result of Result
let response = Response.Result <| Result.Success
match response with
| Result result ->
match result with
| Result.Success -> printfn "Success"
| Result.Failure -> printfn "Failure"
However, when I place some of it in a module:
module Module =
type Result =
| Success
| Failure
type Response =
| Result of Result
let response = Module.Response.Result <| Module.Result.Success // <- ERROR HERE
I get the error:
The field, constructor or member 'Success' is not defined
And it also means that I can't do matching like:
match response with
| Module.Result result ->
match result with
| Module.Result.Success -> printfn "Success"
| Module.Result.Failure -> printfn "Failure"
which fails with
This is not a constructor or literal, or a constructor is being used incorrectly
(To get this to fail, some additional code needs to be added such that Module has a createResponse:
let createResponse b =
if b
then Response.Result <| Result.Success
else Response.Result <| Result.Failure
and response in the code outside the module is then let response = Module.createResponse true.
Shouldn't I be able to achieve the same result regardless of putting some code in a module?
Actually fully qualified name Module.Result.Success should work where first error happens. Compiler confusion comes because you gave the same name to two different things.
This line will compile:
let response = Module.Response.Result <| Module.Success
And you can do pattern matching this way:
match response with
| Module.Result result ->
match result with
| Module.Success -> printfn "Success"
| Module.Failure -> printfn "Failure"
In the last example: you are matching response value against Module.Result with parameter result. It could be only of type Module.Response because the single case of this type - namely Module.Result - has a parameter. And because this parameter is of type Module.Result it could be matched against two cases of this type: Module.Success and Module.Failure. It looks and sound confusing because it is. The same name "Result" is given both to discriminated union type and to the case-id of another discriminate union type. I would change Response type to something like this:
type Response =
| RespResult of Result