This seems like a question that has an ultra simple answer, but I can't think of it:
Is there a built in method, within Result, for:
let (a: Result<'a, 'a>) = ...
match a with
| Ok x -> x
| Error e -> e
No, because this function requires the Ok type and the Error type to be the same, which makes Result less general.
No, there isn't any function which will allow you to do so. But you can easily define it:
[<RequireQualifiedAccess>]
module Result =
let join (value: Result<'a, 'a>) =
match value with
| Ok v -> v
| Error e -> e
let getResult s =
if System.String.IsNullOrEmpty s then
Error s
else
Ok s
let a =
getResult "asd"
|> Result.join
|> printfn "%s"
It doesn't make Result less general (as said by #brianberns), because it's not an instance member. Existence of Unwrap doesn't make Task less general
Update
After more scrupulous searching inside FSharpPlus and FSharpx.Extras I've found necessary function. It's signature ('a -> 'c) -> ('b -> 'c) -> Result<'a,'b> -> c instead of Result<'a, 'a> -> 'a and it's called Result.either in both libraries (source 1 and source 2). So in order to get value we may pass id as both parameters:
#r "nuget:FSharpPlus"
open FSharpPlus
// OR
#r "nuget:FSharpx.Extras"
open FSharpx
getResult "asd"
|> Result.either id id
|> printfn "%s"
Also it's may be useful to define shortcut and call it Result.join or Result.fromEither as it's called in Haskell
Related
Suppose I have a test like this:
module MyTests
open Xunit
open FParsec
open FsUnit.Xunit
open MyParsers
[<Fact>]
let ``pfoo works as expected`` () =
let text = "blahblahblah"
let actual =
match run pfoo text with
| Success (x, _, _) -> Result.Ok x
| Failure (s, _, _) -> Result.Error s
let expected : Result<Foo, string> =
Result.Ok
{
Foo = "blahblahblah"
}
expected
|> should equal actual
open FParsec will shadow Ok so that I need to fully qualify it like Result.Ok.
This is pretty annoying. Is there a good way to "open" Result again so that I can write Ok unqualified?
It's not Result that you need to "open", but Microsoft.FSharp.Core, which is the module in which Result and both its constructors are defined. This module is open by default, but you can open it again to have its definitions closer in the scope:
open Xunit
open FParsec
open FsUnit.Xunit
open MyParsers
open Microsoft.FSharp.Core
Alternatively, you can alias just the Ok identifier:
let Ok = Result.Ok
let x = Ok "foo" // x : Result<string, _>
I prefer this latter method, because it minimizes the impact surface and thus reduces the chance of unexpected surprises.
The downside is that the aliased Ok won't work for pattern matching:
match x with
| Ok y -> ... // This is Ok from FParsec
If you need pattern matching as well, you'll have to alias the matcher too:
let (|Ok|Error|) x = match x with | Result.Ok o -> Ok o | Result.Error e -> Error e
At which point I would probably fall back to reopening the module.
While playing around with map, apply and such to better understand them, the inferred generic types on the following code turned out not be what I expected, but the code still works:
Note: the // comments are written by me so I can show them, but they are the exact copy of the auto annotations printed by Ionide.
let map2 fOk fErr (a : Result<'a,'e>) (b : Result<'b,'e>)= // ('a -> 'b -> 'a0) -> ('e -> 'e -> 'e) -> Result<'a,'e> -> Result<'b,'e> -> Result<'a,'e>
match a, b with
| Ok aOk, Ok bOk -> fOk aOk bOk |> Ok
| Error aErr, Error bErr -> fErr aErr bErr |> Error
| Error aErr, _ -> Error aErr
| _, Error bErr -> Error bErr
let lift2ResultFromMap2 f= // ('a -> 'b -> 'c) -> (Result<'a,'d list> -> Result<'b,'d list> -> Result<'c,'d list>)
map2 f List.append
Here I expected that the return type of map2 is Result<'a0,'e> instead of Result<'a,'e>.
But then when I use it in defining lift2ResultFromMap2 the return type is exactly what I expect, Result<'c,'d list> instead of Result<'a,'d list> that map2 would suggest.
Am I right to think this is an IDE bug? or is there some additional wildcard like meaning to the numbered suffix?
Your constraints are respected first.
Now for your example, it tried to pick 'a for the first unconstrained value, which is the return type of fOk, saw 'a was already in use, so it went with 'a0. So, if you let it pick the constraints with:
(a : Result<_,'e>) (b : Result<_,'e>)
fOk will be inferred to be 'a -> 'b -> 'c. If you have enough values to infer up to 'e, you'll end up seeing an 'e0.
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).
I'm quite sure that I run into some kind of limitation, but I do not understand it:
type IRunner =
abstract member Run : (string -> 'a) -> 'a
type T() =
let run4 doFun = doFun "4"
let run5 doFun = doFun "5"
let parseInt s = System.Int32.Parse(s)
let parseFloat s = System.Double.Parse(s)
let doSomething () =
let i = parseInt |> run4
let f = parseFloat |> run4
f |> ignore
// Make it more generic ->
//let doSomething2 (runner:(string->'a)->'b) =
let doSomething2 runner =
// Error on the following lines with both declarations
let i = parseInt |> runner
let f = parseFloat |> runner
f |> ignore
// Want to do something like
let test () =
doSomething2 run4
doSomething2 run5
// Workaround
let workaround (runner:IRunner) =
let run f = runner.Run f
let i = parseInt |> run
let f = parseFloat |> run
f |> ignore
Can somebody bring some light over this? I did not find any related question, sorry if i duplicated something.
The problem is, if doSomething2 has type ((string->'a) -> 'b) -> unit, then 'a and 'b are fixed during each invocation of doSomething2, which isn't what you want - in your case 'a needs to treated as both int and float during a single invocation of doSomething2.
It seems like what you really want is more like: doSomething2 : (forall 'a. (string -> 'a) -> 'a) -> unit, but that kind of direct universal quantification doesn't exist in F#. As you've discovered, the way to work around this is to use a type with a generic method.
And even if F# did support forall types, as I mentioned in a comment inference still wouldn't be possible. Consider your doSomething2 function - we know that runner needs to be able to take an input of type string -> int to some output type and an input of type string -> float to some (possibly different) output type. Here are several different signatures for doSomething2 that all meet this requirement:
forall 'a. 'a -> 'a
forall 'a. (string -> 'a) -> 'a
forall 'a. 'a -> unit
Note that none of these types is more general than the others, they are all incompatible. In the first case, we could pass id to the function, in the second case, we could pass run4 to it, and in the third case, we could pass ignore to it (but none of those functions is compatible with the other possible signatures!).
I continue to work on a printer for F# quoted expressions, it doesn't have to be perfect, but I'd like to see what is possible. The active patterns in Microsoft.FSharp.Quotations.Patterns and Microsoft.FSharp.Quotations.DerivedPatterns used for decomposing quoted expressions will typically provide MemberInfo instances when appropriate, these can be used to obtain the name of a property, function, etc. and their "declaring" type, such as a module or static class. The problem is, I only know how to obtain the CompiledName from these instances but I'd like the F# name. For example,
> <# List.mapi (fun i j -> i+j) [1;2;3] #> |> (function Call(_,mi,_) -> mi.DeclaringType.Name, mi.Name);;
val it : string * string = ("ListModule", "MapIndexed")
How can this match be rewritten to return ("List", "mapi")? Is it possible?
FYI, here is my final polished solution from Stringer Bell and pblasucci's help:
let moduleSourceName (declaringType:Type) =
FSharpEntity.FromType(declaringType).DisplayName
let methodSourceName (mi:MemberInfo) =
mi.GetCustomAttributes(true)
|> Array.tryPick
(function
| :? CompilationSourceNameAttribute as csna -> Some(csna)
| _ -> None)
|> (function | Some(csna) -> csna.SourceName | None -> mi.Name)
//usage:
let sourceNames =
<# List.mapi (fun i j -> i+j) [1;2;3] #>
|> (function Call(_,mi,_) -> mi.DeclaringType |> moduleSourceName, mi |> methodSourceName);
You can use F# powerpack for that purpose:
open Microsoft.FSharp.Metadata
...
| Call(_, mi, _) ->
let ty = Microsoft.FSharp.Metadata.FSharpEntity.FromType(mi.DeclaringType)
let name = ty.DisplayName // name is List
However, I don't think if it's possible to retrieve function name with powerpack.
Edit:
As hinted by pblasucci, you can use CompilationSourceName attribute for retrieving source name:
let infos = mi.DeclaringType.GetMember(mi.Name)
let att = infos.[0].GetCustomAttributes(true)
let fName =
(att.[1] :?> CompilationSourceNameAttribute).SourceName // fName is mapi