-
Notifications
You must be signed in to change notification settings - Fork 328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple errors/custom error types from a decoded type. #478
Comments
@chrischen Please expand on this, how you'd define |
For custom errors I think maybe provide a semigroup or monoid? This is the shape of the error type I've implemented, where A is the decoded type.
But actually on second thought I think custom errors may not be necessary. If the default error system can just accumulate an arbitrary list of user defined errors that should be fine. It's hard to use the current error reporting unless I just use them as is. If I wrap a decoder then I lose out on granular type errors. I'd like to be able to rely on the error types to do my own logic such as converting the codes to messages. It's also good to have them in one place in case localization is needed. So I'd like to be able to not be limited to string errors. I'm using this tagged type as an example as the value field can be used to pass the context of what the bad input was. Current concatenation of errors should still work right?
|
So I looked through the source and found Here is what I'm trying to get my error handling to look like: Also on a slightly unrelated note, |
Thanks for reporting, fixed in v2.2.6 |
@chrischen this is the more general form of a "decoder" I can think of export interface DecoderT<M extends URIS2, E, A> {
readonly decode: (u: unknown) => Kind2<M, E, A>
} where the error Then you can define some constructors export const fromGuard = <M extends URIS2, E>(M: MonadThrow2C<M, E>) => <A>(
guard: G.Guard<A>,
onError: (u: unknown) => E
): DecoderT<M, E, A> => {
return {
decode: (u) => (guard.is(u) ? M.of(u) : M.throwError(onError(u)))
}
} and primitives export const string = <M extends URIS2, E>(M: MonadThrow2C<M, E>) => (
onError: (u: unknown) => E
): DecoderT<M, E, string> => {
return fromGuard(M)(G.string, onError)
} Here you can find a POC. As you can see from the tests, depending on the chosen |
I pushed this thing further, I got a more fleshed out
@chrischen for what concerns the original issue, Example import * as E from 'fp-ts/lib/Either'
import * as NEA from 'fp-ts/lib/NonEmptyArray'
import { pipe } from 'fp-ts/lib/pipeable'
import * as G from '../src/Guard'
import * as K from '../src/Kleisli'
type MyError = { type: 'string' } | { type: 'NonEmptyString' }
interface Decoder<A> {
readonly decode: (u: unknown) => E.Either<NEA.NonEmptyArray<MyError>, A>
}
const M = E.getValidation(NEA.getSemigroup<MyError>())
const string: Decoder<string> = K.fromRefinement(M)(G.string.is, () => [{ type: 'string' }])
const NonEmptyString: Decoder<string> = pipe(
string,
K.refine(M)(
(s): s is string => s.length > 0,
() => [{ type: 'NonEmptyString' }]
)
)
console.log(NonEmptyString.decode(null)) // { _tag: 'Left', left: [ { type: 'string' } ] }
console.log(NonEmptyString.decode('')) // { _tag: 'Left', left: [ { type: 'NonEmptyString' } ] } |
Awesome! This matches my workaround of wrapping the decoder to return my own error. When do you expect this to be released? |
Closed by #486 |
Would this work together with the default |
Nvm, realized I had to create my own Decoder module, which now works great, thanks for the Kleisli module 👍 |
example above |
🚀 Feature request
I'm wondering what's the best way to do a refinement/decode that may have multiple validations/errors that need to be returned.
In particular I'd like to implement the error handling method as espoused in Railway Oriented Programming: https://fsharpforfunandprofit.com/rop/. Errors are treated as Domain Events and you check for events in a tagged union of the accumulated errors at the end of a workflow/pipeline.
When checking a particular string, such as email, I may want to check for multiple properties and return them as error codes (as opposed to strings). These error codes could wrap data, for example:
{_tag: 'InvalidEmail', value: 'someinvalidemail@adfsdfa'}
Current Behavior
Desired Behavior
Suggested Solution
I would imagine the DecodeError type should be generic to allow the user to specify complex shapes to hold the errors.
Who does this impact? Who is this for?
https://fsharpforfunandprofit.com/rop/ -> Trying to follow patterns espoused here-particularly the stuff around error handling as "messages".
Describe alternatives you've considered
The alternative is to define a new type for each validation case. Eg:
const Email = D.string
const EmailWithAtSign = { decode: (x): x is EmailWithAtSign => ... }
const EmailWithPeriod = { decode: (x): x is EmailWithDomain => ... }
Additional context
Your environment
The text was updated successfully, but these errors were encountered: