Comments (13)
@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
It contains symbol itself and several methods to use class type signatures in statically typed environment
from io-ts.
@OliverJAsh What do you mean with "adapter"? A fold
called on the validation result is the simplest solution I can think of
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)
}
from io-ts.
@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..
from io-ts.
By "adapter" I was thinking of a way to provide an Either
constructor to io-ts. E.g.
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 Either
type in my application. 🤔
from io-ts.
That's food for thought. Thanks for sharing @zerobias!
from io-ts.
But I can't see that happening without all code using fp-ts Either to be abstracted over it
@sledorze We can go even further, we might abstract over the effect returned by validate
and use finally tagless
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
- other Either implementations
- async validations
- "bail out" validations (i.e. compute and return only the first error)
from io-ts.
I wrote a POC supporting asynchronous validations (branch 78
)
Basically it works like this: you provide an instance of MonadType<M>
for some monad M
and you get back a complete runtime type system which works in that monadic context.
// 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
- sync / async validations
- get all errors / only the first
- Either implementation
I wrote 5 instances:
src/either.ts
: (2 instances) for fp-tsEither
: all errors / first error + synchronous validationssrc/taskEither.ts
: (2 instances) for fp-tsTaskEither
: all errors / first error + asynchronous validationstest/sync/funxfix/interface.ts
(1 instance) for funfixEither
: all errors + synchronous validations
from io-ts.
@sledorze This is a first attempt to get a basic idea of performances (using benchmark.js
)
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
# 0.8.2
io-ts x 140,895 ops/sec ±3.03% (79 runs sampled)
io-ts x 140,089 ops/sec ±3.37% (77 runs sampled)
io-ts x 142,319 ops/sec ±2.71% (80 runs sampled)
# 0.9.0
io-ts x 190,435 ops/sec ±0.77% (88 runs sampled)
io-ts x 190,476 ops/sec ±0.67% (87 runs sampled)
io-ts x 182,115 ops/sec ±0.65% (88 runs sampled)
# POC
io-ts x 153,537 ops/sec ±0.68% (86 runs sampled)
io-ts x 155,058 ops/sec ±0.82% (88 runs sampled)
io-ts x 152,557 ops/sec ±0.71% (84 runs sampled)
from io-ts.
@gcanti that's interesting, also wondering what happened between 0.8.2 and 0.9.0..
What is the platform tested? nodeJs? this brings so much questions
is the POC test doing a 'fail fast' path with the monadic 'only the first error' approach?
from io-ts.
what happened between 0.8.2 and 0.9.0
@sledorze it looks like creating a type is less expensive in 0.9.0+
node --version
v8.1.2
Type definitions only
Code
import * 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 })
Results
0.8.2
277,827 ops/sec ±2.50% (72 runs sampled)
0.9.0
431,908 ops/sec ±0.65% (88 runs sampled)
POC
424,371 ops/sec ±0.63% (88 runs sampled)
Validations only
Code
import * 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 })
Results
0.8.2
210,008 ops/sec ±0.63% (87 runs sampled)
0.9.0
205,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)
from io-ts.
@gcanti is there some differences on all/first error for successful matches? (no error generated?)
from io-ts.
@sledorze not that much
All
88,530 ops/sec ±1.46% (86 runs sampled)
First
94,668 ops/sec ±0.94% (89 runs sampled)
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)
valid payload (no errors) x 178,816 ops/sec ±1.22% (89 runs sampled)
Context and Errors as Array
s (POC all errors)
invalid payload x 154,399 ops/sec ±0.97% (85 runs sampled)
valid payload (no errors) x 91,946 ops/sec ±0.98% (87 runs sampled)
Context and Errors as LinkedList
s + optimized zipEithers
(POC all errors)
invalid payload x 509,834 ops/sec ±0.52% (89 runs sampled)
valid payload (no errors) x 148,614 ops/sec ±1.92% (85 runs sampled)
from io-ts.
@gcanti indeed, that's quite a lot of performance gains.
btw are your libs integrated into a CI ?
It may be interesting to track down that automatically, like so:
https://travis-ci.org/lloydmeta/frunk/jobs/172486500#L398
I may help on that (if we decide to integrate on a CI first).
from io-ts.
Related Issues (20)
- Behavior changes and types are incorrect based on `intersection` array order
- Intersection with record whose keys are a custom type HOT 1
- io-ts recursion use issue HOT 4
- Subpath imports in ESM mode HOT 5
- Clarification question - how to work with the type of codecs themselves? HOT 1
- Surprising acceptance of various inputs HOT 1
- ReadonlyNonEmptyArray can't be used in a mapped type HOT 1
- How to generate documentation for types generated with `t.TypeOf` HOT 1
- Difficulties with generic serialisable type HOT 2
- [Question] Typing a generic mapped union HOT 1
- Inference error for `toString` property in intersection types with TypeScript 4.9.5 HOT 1
- `t.TypeOf<keyof<o>>` should return a string union type, not a numeric union type.
- PSA: TS 5.1 can break using this library HOT 1
- Intersection of Function with Object doesn't validate correctly
- t.Int: use Number.isSafeInteger instead of Number.isInteger
- non-enumerable records with extra keys do not pass `io-ts.record.is`, contrary to TypeScript types HOT 12
- partially enumerable record missing enumerable keys passes `record.is`, contrary to TypeScript types
- Records are stripped when encoding with t.record(NumberFromString, xxx) HOT 2
- Question: Convert a form to a type with different structure
- Incorrect behaviour when used with browser geolocation coordinates object
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from io-ts.