This project brings railway-oriented programming to .NET.
Documentation: http://fsprojects.github.io/Chessie
The default maintainer account for projects under "fsprojects" is @fsprojectsgit - F# Community Project Incubation Space (repo management)
Railway-oriented programming for .NET
Home Page: http://fsprojects.github.io/Chessie/
License: The Unlicense
This project brings railway-oriented programming to .NET.
Documentation: http://fsprojects.github.io/Chessie
The default maintainer account for projects under "fsprojects" is @fsprojectsgit - F# Community Project Incubation Space (repo management)
I'm interested how to get an error list of combined validation failures.
In ROP example by Scott he is using plus
or &&&
.
I assume in Chessie this is apply
or <*>
, but I'm not sure how to use it.
I didn't see any example of using it, and no test on this function.
Is there a possibility for somebody to show example of using (as in your docs combinedValidation
) but result to be, all errors that happened in all validation combined together, because with bind >>
only first error is returned?
Thanks.
This is not a problem but a feature I'm proposing. I don't know whether it can be easily reproduced with current functionality. I could make a PR if you want me to, but I wanted to ask here before going forward.
I have the following situation: I want to be able to collect a number of validation results in the form of Result<unit, ‘error>
and obtain a condensed validation result of the same type.
So I would like to be able to do:
let result = trial {
let validations = [ validate1; validate2; validate3; … ]
return! Validations |> collectErrors
}
Where:
module Chessie.ErrorHandling.Trial
...
let mergeErrors result1 result2 =
match result1, result2 with
| Ok ((), errors1), Ok ((), errors2) ->
Ok ((), errors1 @ errors2)
| Ok ((), errors1), Bad (errors2)
| Bad (errors1), Ok ((), errors2)
| Bad (errors1), Bad (errors2) ->
Bad (errors1 @ errors2)
let collectErrors =
Seq.reduce mergeErrors
It could be further generalized for collecting all results and errors. For example:
let mergeResults reducer result1 result2 =
match result1, result2 with
| Ok (r1, errors1), Ok (r2, errors2) ->
Ok (reducer r1 r2, errors1 @ errors2)
| Ok (_, errors1), Bad (errors2)
| Bad (errors1), Ok (_, errors2)
| Bad (errors1), Bad (errors2) ->
Bad (errors1 @ errors2)
let collectResults reducer =
Seq.reduce (mergeResults reducer)
Please direct me on the right direction if this does not make any sense, or indicate if you want me to start a PR.
Thanks
Before raising my grasp (and I've still a lot to understand and learn) of monads, I was always confused by return
monadic operation.
For what I understand now the return
operation (called sometime unit
if I'm not wrong) inject a value into the monadic abstraction.
In fact, in Haskell the Control.Monad type class defines return with this signature return :: a -> m a
.
In FSharp that lacks HKT:
type Maybe = Just x | Nothing
module Maybe =
return x = Just x
// ...
we must define everything for each specific monad. Unless creating a strange convoluted and not idiomatic implementations, I guess...
So also if returnOrFail sense is clear it's name doesn't clash with this concept? It's not better something like getOrFail
? (E.g.: I'm agree to a more domain specific return like Succeed and FailWith.
I know that computations expressions have a ReturnFrom
for allowing a wrapped value escape the monad, and in fact now I found it confusing...
I'm not here to criticize, just to hear thoughs and learn more about the subject. If I wouldn't like this library, I'd never ported it to C# for direct source inclusion as annunced in issue #25 (library that still need refinements and comments...).
My best regards,
Giacomo
It is not showing properly the forking repo elements and links
Please provide the steps required to reproduce the problem
Step A
Step B
It should Represent very nicely to fork this repository to beginners
Please provide a description of the behavior you expect.
Please provide a description of the actual behavior you observe.
Please provide a description of any known workarounds.
Currently you have the following:
let inline apply wrappedFunction result =
match wrappedFunction, result with
| Ok(f, msgs1), Ok(x, msgs2) -> Ok(f x, msgs1 @ msgs2)
| Fail errs, Ok(_, msgs) -> Fail(errs)
| Ok(_, msgs), Fail errs -> Fail(errs)
| Fail errs1, Fail errs2 -> Fail(errs1)
When looking at Scott his ROP implementation he has:
let applyR f result =
match f,result with
| Success (f,msgs1), Success (x,msgs2) ->
(f x, msgs1@msgs2) |> Success
| Failure errs, Success (_,msgs)
| Success (_,msgs), Failure errs ->
errs @ msgs |> Failure
| Failure errs1, Failure errs2 ->
errs1 @ errs2 |> Failure
Could we change it in Chessie to either one of these two:
This is identical to Scott's version
let inline apply wrappedFunction result =
match wrappedFunction, result with
| Ok(f, msgs1), Ok(x, msgs2) -> Ok(f x, msgs1 @ msgs2)
| Fail errs, Ok(_, msgs)
| Ok(_, msgs), Fail errs -> Fail(errs @ msgs)
| Fail errs1, Fail errs2 -> Fail(errs1 @ errs2)
On fail, don't take the Ok messages along
let inline apply wrappedFunction result =
match wrappedFunction, result with
| Ok(f, msgs1), Ok(x, msgs2) -> Ok(f x, msgs1 @ msgs2)
| Fail errs, Ok(_, msgs)
| Ok(_, msgs), Fail errs -> Fail(errs)
| Fail errs1, Fail errs2 -> Fail(errs1 @ errs2)
When looking at the code for apply, it seems to only concat all the messages when either both results are Ok or both are Bad. Should it concat all the message regardless?
Chessie/src/Chessie/ErrorHandling.fs
Lines 105 to 106 in 7c31c30
I am trying to return a list of failure messages when using apply
createThing <!> firstOrFail <*> secondOrFail <*> thirdOrFail
And instead of giving me all the messages it just gives me the first after a failure.
If I rewrite the middle two cases of apply I appear to get what I am after.
So is this a bug, enhancement, or am I actually asking for different function other then apply, like applyWithAllMessage <*+>?
/// If the wrapped function is a success and the given result is a success the function is applied on the value.
/// Otherwise the exisiting error messages are propagated.
let inline apply wrappedFunction result =
match wrappedFunction, result with
| Ok(f, msgs1), Ok(x, msgs2) -> Ok(f x, msgs1 @ msgs2)
| Bad errs, Ok(_, msgs) -> Bad(errs @ msgs)
| Ok(_, msgs), Bad errs -> Bad(errs @ msgs)
| Bad errs1, Bad errs2 -> Bad(errs1 @ errs2)
/cc @mexx @theimowski
mergeMessages appends messages for error but prepends them for success
mergeMessages should be consistent; IMHO it should always append them, as this is merging messages into an existing Result stream.
let inline mergeMessages msgs result =
let inline fSuccess (x, msgs2) = Ok(x, msgs @ msgs2) // prepends
let inline fFailure errs = Bad(errs @ msgs) // appends
either fSuccess fFailure result
I have a gist that I include in most of my FSharp projects. I wanted to ask if the maintainer of this project is willing to expand the AsyncResult type to include utility methods like Trial has (See gist link). I got most if not all the code from Scott Wlaschin after I read his DDD with FSharp book.
Just a thought =)
https://www.nuget.org/api/v2/package/Chessie/0.6.0
File version is only applied to net40 assembly, see file properties.
Hi there,
thanks for sharing this wonderful library. I've also appreciated C# extension methods, but I think (personal opinion) that some C# developer could prefer including Result<TSuccess,TMessage>
directly at source level (without referencing an assembly).
For this reason I've ported your work to C# native implementation. I've skipped async part, since is mainly for computation expressions and in C# Task<Result<TSucc,TMsg>>
will suffice.
This is the link to the project: https://github.com/gsscoder/railwaysharp. (I've also included test code from your project).
Feel free to do with it whatever you want...
Regards,
Giacomo
The LINQ extension method SelectMany
yields duplicated messages when the LINQ query syntax is used as this test shows:
[Test]
public void Test()
{
Func<Result<string, string>> f = () =>
from a in Result<string, string>.FailWith("fail")
from b in Result<string, string>.Succeed("succeed")
from c in Result<string, string>.Succeed("succeed")
select a + b + c;
f().Match(
ifSuccess: (s, list) => Assert.Fail(),
ifFailure: list => Assert.That(list, Is.EquivalentTo(new[] { "fail" })));
}
The test fails with this message:
Expected: equivalent to < "fail" > But was: < "fail", "fail", "fail", "fail" >
To compose results in a nice way, more operators would be useful.
The one I always end up implementing is a flipped map/(<!>):
// fromProcess returns a Result<string,ProcessMessage>
// with <!> operator
let devices' () =
Process.exec "." Adb.adb "devices"
|> fromProcess
|> (<!>) Strings.toLines
|> (<!>) (Seq.choose device)
|> (<!>) (Seq.map ((flip>>uncurry) model))
// with flipped <!>
let inline (<**>) x y = (flip (<!>)) x y
let devices () =
Process.exec "." Adb.adb "devices"
|> fromProcess
<**> Strings.toLines
<**> (Seq.choose device)
<**> (Seq.map ((flip>>uncurry) model))
Because I think this is a better forum than twitter!
@tpetricek do you mind explaining your opposition to using ROP based names? I'm not sure I'm seeing the issue yet, especially if we have a two sentence byline of "this is an either monad under the hood" somewhere.
It feels like it throws away a lot of Scott's good work on explaining things in nice easy to understand language.
The Fail
active recognizer introduced in #15 by @pblasucci shadows the Fail
union case.
It actually introduces "incomplete pattern matches on this expression" compiler warnings for code like
match result with
| Ok (x, msgs) -> ...
| Fail (msgs) -> ...
Prior to this change the match was complete.
Fortunately the change only confuses the compiler and do not change the behavior of the pattern match.
We should require qualified access on one of the two constructs or rename one of them to avoid the name clash.
To create a Result in Chessie, you have to explicitly specify both generic types e.g.: -
Result<MyMessage, string>.Succeed(myMessage)
This could be simplified by having static methods on a non-generic Result class e.g.
Result.Succeed<string>(myMessage)
support .NET Core
i added the issue because i am working 1)
I started by adding XML docs. Would like to see more docs and links to videos and articles.
Also samples (maybe taken from Paket).
Hello, I have a weird issue on chessie when using paket.core
Fsharp.Core MissingMethodException when using Paket.Core with latest version of Chessie
Install Paket.Core in a VSIX solution.
Try using the FindOutdated or Update command from the library --> Fail with MissingMethodException
Update the target framework for Chessie F# project from 4.0 to 4.5
(I tried this using paket.local for chessie)
No more exception.
Maybe a upgrade to framework 4.5 could be done on the main project, knowing that Chessie.CSharp.Test and Chessie.Tests are already on 4.5
Thanks,
Thomas
Interestingly, it IS in the directory alongside other HTML files.
Maybe this is a caching/propagation thing?
I want to use Chessie for error handling, but Trial.either does not make sense to me.
Try to compile this:
open System
open Chessie.ErrorHandling
type Error =
| InvalidEmail of string
| DatabaseError of Exception
let validateEmail (email:string) =
if email.Contains("@")
then ok email
else fail (InvalidEmail "@ is missing")
let sendEmail (email:string) =
printfn "sending email to %s..." email
let handleError (error:Error) =
match error with
| InvalidEmail email -> printfn "bad email: %s" email
| DatabaseError ex -> printfn "db error: %A" ex
"hello@world"
|> validateEmail
|> Trial.either sendEmail handleError
Code should compile.
If the email is valid, then sendEmail should be called, else handleError
Compiler error 1:
Type mismatch. Expecting a
'string * Error list -> 'a'
but given a
'string -> unit'
The type 'string * Error list' does not match the type 'string'F# Compiler(1)
Compiler error 2:
Type mismatch. Expecting a
'Error list -> 'a'
but given a
'Error -> unit'
The type 'Error list' does not match the type 'Error'
If I change the signature of the two functions, it works.
let sendEmail (email:string, errors: Error list) =
printfn "sending email to %s..." email
let handleError (errors:Error list) =
for error in errors do
match error with
| InvalidEmail email -> printfn "bad email: %s" email
| DatabaseError ex -> printfn "db error: %A" ex
But this seems like an unneccessary workaround for me.
1.) Why would I want a (always empty) error list in the success handler?
2.) How could there be more than one error in the failure handler?
I was expecting that the Result always contains the first error.
My expectations are based on this article: https://fsharpforfunandprofit.com/posts/recipe-part2/
Forked the repository here: https://github.com/kellerd/Chessie
Tried to get Extensions working in .NET.
Added a test project for it:
https://github.com/kellerd/Chessie/tests/Chessie.VisualBasic.Test/Chessie.VisualBasic.Test.vbproj
The easiest fix I found was to add [<assembly: ExtensionAttribute()>]
to Assembly.fs and include it into the fs project.
Ran into an issue when using FAKE, where it won't let me add an attribute in your genFSAssemblyInfo section without specifying parameters to the attribute.
I added an issue to FAKE in the mean time, maybe it'll get some traction, to add the feature
fsprojects/FAKE#1115, before I try to create a PR myself
Original problem:
https://github.com/kellerd/Chessie/tree/master
Potential fix:
https://github.com/kellerd/Chessie/tree/Intermediatefix
Unless someone has come across this before or has more experience with it.
https://www.nuget.org/api/v2/package/Chessie/0.6.0
Assembly info is only applied to net40 assembly, see file properties.
I was wanting some adapter functions that lived in the AsyncResult space. I shoehorned these into one of the projects I'm currently working on.
// ('a -> Result<'b,'c>) -> ('a -> AsyncResult<'b,'c>)
let resultToAsync (f : 'a -> Result<'b,'c>) x = x |> f |> Async.singleton |> AR
// ('a -> 'b) -> (AsyncResult<'a,'c> -> AsyncResult<'b,'c>)
let liftAsyncResult (oneTrackFunction : 'a -> 'b) (twoTrackInput : AsyncResult<'a,'c>) = asyncTrial {
let! twoTrackResult = twoTrackInput
return oneTrackFunction twoTrackResult
}
// ('a -> AsyncResult<'b,'c>) -> (AsyncResult<'a,'c> -> AsyncResult<'b,'c>)
let bindAsyncResult (switchFunction : 'a -> AsyncResult<'b,'c>) (twoTrackInput : AsyncResult<'a,'c>)= asyncTrial {
let! twoTrackResult = twoTrackInput
return! switchFunction twoTrackResult
}
The use case I had in mind was:
let updateSomeRecord = resultToAsync validate
>> bindAsyncResult queryDatabase
>> liftAsyncResult saveResults
>> Async.ofAsyncResult
Probably not the best implementation and names need to change. Should I make a PR for this?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.