-
Notifications
You must be signed in to change notification settings - Fork 330
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
Interop with other Either types #78
Comments
@OliverJAsh What do you mean with "adapter"? A import * as t from 'io-ts'
import { Either, Left, Right } from 'funfix-core'
type FunFixValidation<T> = Either<Array<t.ValidationError>, T>
function myvalidate<T>(v: any, type: t.Type<T>): FunFixValidation<T> {
return t.validate(v, type).fold<FunFixValidation<T>>(Left, Right)
} |
@gcanti I think @OliverJAsh was maybe thinking about something without the overhead to write the call.. But I can't see that happening without all code using fp-ts Either to be abstracted over it, which seems to bring a lot more overhead.. |
By "adapter" I was thinking of a way to provide an t.validate(value, type, {
getEither: () => FunfixEither
}) Essentially I want to avoid depending on (and therefore bundling) fp-ts when using io-ts if I already have another |
@OliverJAsh no need to create any unique method exactly for Eithers. Js provides a safe way to make custom protocols via Symbol interfaces. Therefore it is possible to store any type meta info in fields and methods inside symbol property to tell other modern functional libraries about your typeclasses in a conventional way And we already have success story. Streaming libraries are uses symbol-observable to have interop with each other. import { from } from 'most'
import { createStore } from 'redux'
const someStore = createStore(s => s) // typical redux store...
const storeUpdates$ =
from(someStore) // ...also supports interconnection via Symbol.observable So why not to try it too? I already made an implementation of that concept and using it in production code https://github.com/zerobias/apropos/tree/develop/packages/signature |
That's food for thought. Thanks for sharing @zerobias! |
@sledorze We can go even further, we might abstract over the effect returned by export type Errors = Array<ValidationError>
export interface MonadThrow<E, M> extends Monad<M> {
throwError: <A>(e: E) => HKT<M, A>
}
export interface MonadType<M> extends MonadThrow<Errors, M> {
zipWith: <A, B, C>(f: (a: A, b: B) => C) => (fa: HKT<M, A>, lazyfb: () => HKT<M, B>) => HKT<M, C>
}
export class Type<M, S, A> {
readonly _A: A
readonly _S: S
readonly _M: M
constructor(
readonly name: string,
readonly is: (v: any) => v is A,
readonly validate: (s: S, c: Context) => HKT<M, A>,
readonly serialize: (a: A) => S
) {}
}
export class StringType<M> extends Type<M, any, string> {
readonly _tag: 'StringType' = 'StringType'
constructor(M: MonadType<M>) {
super(
'number',
(v): v is string => typeof v === 'string',
(s, c) => (this.is(s) ? M.of(s) : M.throwError([{ value: s, context: c }])),
identity
)
}
}
export const getStringType = <M>(M: MonadType<M>): StringType<M> => new StringType(M) It would allow
|
I wrote a POC supporting asynchronous validations (branch Basically it works like this: you provide an instance of // core.ts
export interface MonadThrow<E, M> extends Monad<M> {
throwError: <A>(e: E) => HKT<M, A>
}
export interface MonadType<M> extends MonadThrow<Array<ValidationError>, M> {
zipWith: <A, B, C>(f: (a: A, b: B) => C) => (fa: HKT<M, A>, lazyfb: () => HKT<M, B>) => HKT<M, C>
attempt: <A>(fx: HKT<M, A>, lazyfy: () => HKT<M, A>) => HKT<M, A>
}
export type Is<A> = (v: any) => v is A
export type Validate<M, S, A> = (s: S, context: Context) => HKT<M, A>
export type Serialize<S, A> = (a: A) => S
export class Type<M, S, A> {
readonly '_A': A
readonly '_S': S
readonly '_M': M
constructor(
readonly name: string,
readonly is: Is<A>,
readonly validate: Validate<M, S, A>,
readonly serialize: Serialize<S, A>
) {}
}
export const getTypeSystem = <M>(M: MonadType<M>): TypeSystem<M> => {
...
} So depending on the instance you pass, you can choose
I wrote 5 instances:
|
@sledorze This is a first attempt to get a basic idea of performances (using Code suite
.add('io-ts', function() {
const T = t.type({
a: t.string,
b: t.number,
c: t.array(t.boolean),
d: t.tuple([t.number, t.string])
})
t.validate({}, T)
}) Results
|
@gcanti that's interesting, also wondering what happened between 0.8.2 and 0.9.0.. |
@sledorze it looks like creating a type is less expensive in 0.9.0+
Type definitions onlyCodeimport * as Benchmark from 'benchmark'
import * as t from 'io-ts'
const suite = new Benchmark.Suite()
suite
.add('io-ts', function() {
t.type({
a: t.string,
b: t.number,
c: t.array(t.boolean),
d: t.tuple([t.number, t.string])
})
})
.on('cycle', function(event: any) {
console.log(String(event.target))
})
.on('complete', function(this: any) {
console.log('Fastest is ' + this.filter('fastest').map('name'))
})
.run({ async: true }) Results0.8.2277,827 ops/sec ±2.50% (72 runs sampled) 0.9.0431,908 ops/sec ±0.65% (88 runs sampled) POC424,371 ops/sec ±0.63% (88 runs sampled) Validations onlyCodeimport * as Benchmark from 'benchmark'
import * as t from 'io-ts'
const suite = new Benchmark.Suite()
const T = t.type({
a: t.string,
b: t.number,
c: t.array(t.boolean),
d: t.tuple([t.number, t.string])
})
const payload = { c: [1], d: ['foo'] }
suite
.add('io-ts', function() {
t.validate(payload, T)
})
.on('cycle', function(event: any) {
console.log(String(event.target))
})
.on('complete', function(this: any) {
console.log('Fastest is ' + this.filter('fastest').map('name'))
})
.run({ async: true }) Results0.8.2210,008 ops/sec ±0.63% (87 runs sampled) 0.9.0205,194 ops/sec ±0.66% (85 runs sampled) POC (all errors)158,106 ops/sec ±0.56% (87 runs sampled) POC (first error)745,731 ops/sec ±0.60% (88 runs sampled) |
@gcanti is there some differences on all/first error for successful matches? (no error generated?) |
@sledorze not that much All It looks like there are some low hanging fruits, for example this is the results using linked lists Base reference (0.9.0)invalid payload x 197,033 ops/sec ±0.84% (82 runs sampled) Context and Errors as
|
@gcanti indeed, that's quite a lot of performance gains. It may be interesting to track down that automatically, like so: I may help on that (if we decide to integrate on a CI first). |
It would be nice if io-ts worked with different
Either
types. Perhaps we could provide an adapter? If we did this, we'd need to think about which parts of theEither
type are required.For context, on one project I am experimenting with using the Either type from funfix: https://funfix.org/, and I want to avoid duplicate
Either
types in my project.The text was updated successfully, but these errors were encountered: