gcanti / fp-ts-contrib Goto Github PK
View Code? Open in Web Editor NEWA community driven utility package for fp-ts
Home Page: https://gcanti.github.io/fp-ts-contrib/
License: MIT License
A community driven utility package for fp-ts
Home Page: https://gcanti.github.io/fp-ts-contrib/
License: MIT License
Please refer to #37.
The following code works:
interface Console<F extends URIS> {
putStrLn: (message: string) => Kind<F, void>
getStrLn: Kind<F, string>
}
// ask something and get the answer
const ask = (question: string): Kind<F, string> =>
Do(F)
.do(F.putStrLn(question) as Kind<F, unknown>)
.bind('answer', F.getStrLn)
.return(({ answer }) => answer)
When I remove as Kind<F, unknown>
from the code, it emits this error:
Argument of type 'Kind<F, void>' is not assignable to parameter of type 'Kind<F, unknown>'.
Type 'URItoKind<void>[F]' is not assignable to type 'URItoKind<unknown>[F]'.
Type 'URItoKind<void>' is not assignable to type 'URItoKind<unknown>'.
Types of property 'Eq' are incompatible.
Type 'Eq<void>' is not assignable to type 'Eq<unknown>'.
Type 'unknown' is not assignable to type 'void'.
Type 'URItoKind<void>[F]' is not assignable to type '(Eq<unknown> & Ord<unknown> & None & IO<unknown> & Task<unknown>) | (Eq<unknown> & Ord<unknown> & Some<unknown> & IO<unknown> & Task<unknown>)'.
Type 'IO<void> | Task<void> | None | Eq<void> | Ord<void> | Some<void>' is not assignable to type '(Eq<unknown> & Ord<unknown> & None & IO<unknown> & Task<unknown>) | (Eq<unknown> & Ord<unknown> & Some<unknown> & IO<unknown> & Task<unknown>)'.
Type 'IO<void>' is not assignable to type '(Eq<unknown> & Ord<unknown> & None & IO<unknown> & Task<unknown>) | (Eq<unknown> & Ord<unknown> & Some<unknown> & IO<unknown> & Task<unknown>)'.
Type 'IO<void>' is not assignable to type 'Eq<unknown> & Ord<unknown> & None & IO<unknown> & Task<unknown>'.
Type 'IO<void>' is not assignable to type 'Eq<unknown>'.
Type 'URItoKind<void>[F]' is not assignable to type 'Eq<unknown> & Ord<unknown> & Some<unknown> & IO<unknown> & Task<unknown>'.
Type 'IO<void> | Task<void> | None | Eq<void> | Ord<void> | Some<void>' is not assignable to type 'Eq<unknown> & Ord<unknown> & Some<unknown> & IO<unknown> & Task<unknown>'.
Type 'IO<void>' is not assignable to type 'Eq<unknown> & Ord<unknown> & Some<unknown> & IO<unknown> & Task<unknown>'.
Property 'equals' is missing in type 'IO<void>' but required in type 'Eq<unknown>'.
Type 'URItoKind<void>[F]' is not assignable to type 'Eq<unknown>'.
Type 'IO<void> | Task<void> | None | Eq<void> | Ord<void> | Some<void>' is not assignable to type 'Eq<unknown>'.
Property 'equals' is missing in type 'IO<void>' but required in type 'Eq<unknown>'.ts(2345)
Eq.d.ts(32, 14): 'equals' is declared here.
Eq.d.ts(32, 14): 'equals' is declared here.
This happens when the HKT's return type is
void
(Kind<F, void>
)
This should work (without as Kind<F, unknown>
)
// ask something and get the answer
const ask = (question: string): Kind<F, string> =>
Do(F)
.do(F.putStrLn(question))
.bind('answer', F.getStrLn)
.return(({ answer }) => answer)
Please refer to #37.
No idea
"compilerOptions": {
/* Basic Options */
"target": "es2020",
"module": "CommonJS",
"outDir": "./dist",
/* Strict Type-Checking Options */
"strict": true,
/* Module Resolution Options */
"moduleResolution": "Node",
/* Experimental Options */
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"skipLibCheck": true,
},
Which versions of fp-ts-contrib are affected by this issue? Did this work in previous versions of fp-ts-contrib?
Software | Version(s) |
---|---|
fp-ts | 2.3.1 |
fp-ts-contrib | 0.1.8 |
TypeScript | 3.7.4 |
Do you want to request a feature or report a bug?
I'm not sure whether it qualifies as a bug, it may be my lack of understanding.
What is the current behaviour?
Reviewing the example code at the test directory of this project I have attempted to use Free. According to the test code the file where the monad Expr is defined needs to contain the following bit of code (or so it seems):
type ExprF<A> = Add<A> | Lit<A>
declare module 'fp-ts/lib/HKT' {
interface URItoKind<A> {
Expr: ExprF<A>
}
}
Without it, the compiler displays an error No overload matches this call
. This works well when only one file in the project defines an ExprF but when there is more than one occurrence they step on each other. So, for instance having two files on the same project:
// addLit.ts
type ExprF<A> = Add<A> | Lit<A>
declare module 'fp-ts/lib/HKT' {
interface URItoKind<A> {
Expr: ExprF<A>
}
}
and
// fooBar.ts
type ExprF<A> = Foo<A> | Bar<A>
declare module 'fp-ts/lib/HKT' {
interface URItoKind<A> {
Expr: ExprF<A>
}
}
I get an error Subsequent property declarations must have the same type. Property 'Expr' must be of type 'Expr<A>', but here has type 'Exp<A>'
If the current behaviour is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://codesandbox.io/ or similar.
Create a different Free Expr on two files in the same package as described above.
What is the expected behavior?
Be able to have different Expr in the same project.
Which versions of fp-ts-contrib, and which browser and OS are affected by this issue? Did this work in previous versions of fp-ts-contrib?
0.1.6 ~ NixOs 19.09 ~ I don't know
Related #35
Do does not support destructuring binding.
do
(a, b) <- m
let (a, b) = x
...
.bindD(m.map(x => ({ a: x.a, b: x.b }))) === .bind("tmp", m).letL("a" ,({ tmp }) => tmp.a).letL("b" ,({ tmp }) => tmp.b)
.bindDL(() => m.map(x => ({ a: x.a, b: x.b }))) === .bindL("tmp", () => m).letL("a" ,({ tmp }) => tmp.a).letL("b" ,({ tmp }) => tmp.b)
.letD({ a: 1, b: 2 }) === .sequenceS({ a: M.of(1), b: M.of(2) })
.letDL(() => ({ a: 1, b: 2 })) === .sequenceSL(() => ({ a: M.of(1), b: M.of(2) }))
As a fp-ts-contrib
user, I will need to import modules like fp-ts-contrib/lib/Do
or fp-ts-contrib/es6/Do
.
Ideally, it would be great to just import the module directly without prefix like fp-ts-contrib/Do
that fp-ts
already shipped in 2.8.0.
Maybe we can follow what fp-ts
has done in gcanti/fp-ts#1241 to fix this.
fp-ts-contrib
users
Software | Version(s) |
---|---|
fp-ts | 2.8.0 |
fp-ts-contrib | 0.5.1 |
TypeScript | 3.9.2 |
Related gcanti/fp-ts#1004
The same as gcanti/io-ts-types#129 but for this library.
I removed the extra information for the issue because the issue is self explantory via the issue.
I have a separate 'common' npm package that has 'fp-ts' dependency. I use that 'common' package in my main application, which has also a dependency to 'fp-ts' as long as dependency to 'fp-ts-contrib'.
The issue I have is that in this setup Do notation behaves in a weird way. I get some errors that do not make sense (The screenshot is attached).
I would expect it to be working the same even if fp-ts is initialized in underneath dependencies themselves.
Software | Version(s) |
---|---|
fp-ts | 2.10.5 |
fp-ts-contrib | 0.1.26 |
TypeScript | 4.2.3 |
As any other data-structure in the fp-ts library, I expect Zipper
to support ReadonlyArray
. This is important because most people that use Zipper
will want to work with immutable data-structures.
Zipper
has toArray
. Doesn't have toReadonlyArray
.
Zipper
should have a toReadonlyArray
of type: <A>(z: Zipper<A>) => ReadonlyArray<A>
.
Just alias the new function to toArray
.
export const toReadonlyArray: <A>(fa: Zipper<A>) => ReadonlyArray<A> = toArray
PR #76 does exactly that.
It's not a breaking change, so it shouldn't impact anyone. It's for people who cares about immutability.
The alternative is to use fromArray
from fp-ts/ReadonlyArray
after each toArray
from fp-ts-contrib/Zipper
:
import * as A from 'fp-ts/ReadonlyArray'
import * as Z from 'fp-ts-contrib/Zipper'
const as = pipe(
Z.of('a'),
Z.toArray,
A.fromArray,
)
This is what I currently do, but it's not nice and not clean.
Generally I think it's better to have the implementation of Zipper
uses ReadonlyArray
.
Software | Version(s) |
---|---|
fp-ts | 2.10.4 |
fp-ts-contrib | 0.1.21 |
TypeScript | 4.2 |
fo-ts bindings for AsyncIterable module, which is lazy lists from the promise land.
Haven't considered if we should DIY with a mix like Iterable<Task<A>>
, which might be the preferred solution.
I feel like AsyncIterable
exists because Promises are eager.
Software | Version(s) |
---|---|
fp-ts | |
fp-ts-contrib | |
TypeScript |
regex.test
on global or sticky regular expressions will mutate the lastIndex
, therefore using test
is not a pure function.
For example this is what sanctuary is doing and this
I suspect we should do something similar here too for example this is what I have:
/**
* Remembers `lastIndex` of the provided `regex` and resets it back after running the thunk
*/
export function withRegex<T>(regex: RegExp, thunk: () => T): T {
const lastIndex = regex.lastIndex;
const result = thunk();
regex.lastIndex = lastIndex;
return result;
}
/**
* A pure function version of regex.test(s). i.e. this will not change the `lastIndex` prop of the regex.
* see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test#using_test_on_a_regex_with_the_global_flag
*/
export function regexTest(regex: RegExp, s: string) {
return withRegex(regex, function () {
return regex.test(s);
});
}
In Scala, you can do a for-comprehension with a if
:
for {
n <- Some(12)
if n < 10
m <- Some(n * 2)
} yield m
Would it be possible to do this with a Do
?
Maybe with this syntax:
Do(option)
.bind('n', some(12))
.filter(({ n }) => n < 10)
.bindL('m', ({ n }) => some(n * 2))
.return(({ m }) => m)
In Scala, the for-comprehension checks that the given Monad has a withFilter
method.
Either
Future
, the failed value is:
Future(Failure(NoSuchElementException("Future.filter predicate is not satisfied")))
Task
or a TaskEither
. Maybe with a TaskOption
?I'm trying to write a small library to make it easier to create types that are existentially quantified. I'm using the idea of a type interface F<A> { foo : a, ext : exists E. E }
(pseudocode) can be implemented as interface Fe<A,E> { foo : a, ext : E }
and type F<A> = <R>(cont : <E>(x : Fe<A,E>) => R) => R
. My current implementation can be found here: https://github.com/theNerd247/tsfreeapp/blob/master/existential.ts
liftExt
compiles just fine. However mapExt
which is supposed to make lifting functions that modify the underlying containers of existentially quantified types easier. However I get the following compiler error and I'm not sure where to start on fixing this any ideas?
existential.ts:34:3 - error TS2322: Type 'Existential2<"Either", unknown>' is not assignable to type 'Existential<F>'.
Types of property 'runExt' are incompatible.
Type '<R>(cont: <E>(t: Either<unknown, E>) => R) => R' is not assignable to type '<R>(cont: <E>(t: Kind<F, E>) => R) => R'.
Types of parameters 'cont' and 'cont' are incompatible.
Types of parameters 't' and 't' are incompatible.
Type 'Either<unknown, E>' is not assignable to type 'Kind<F, unknown>'.
Type 'Left<unknown>' is not assignable to type 'Kind<F, unknown>'.
34 return liftExt(x.runExt(f))
~~~~~~~~~~~~~~~~~~~~~~~~~~~
existential.ts:34:27 - error TS2345: Argument of type '<E>(a: Kind<F, E>) => Kind<F, E>' is not assignable to parameter of type '<E>(t: Kind<F, E>) => Kind<F, unknown>'.
Type 'Kind<F, E>' is not assignable to type 'Kind<F, unknown>'.
Type 'URItoKind<E>[F]' is not assignable to type 'URItoKind<unknown>[F]'.
Type 'URItoKind<E>' is not assignable to type 'URItoKind<unknown>'.
Types of property 'Eq' are incompatible.
Type 'Eq<E>' is not assignable to type 'Eq<unknown>'.
Type 'unknown' is not assignable to type 'E'.
Type 'URItoKind<E>[F]' is not assignable to type '(Eq<unknown> & Ord<unknown> & None) | (Eq<unknown> & Ord<unknown> & Some<unknown>)'.
Type 'None | Eq<E> | Ord<E> | Some<E>' is not assignable to type '(Eq<unknown> & Ord<unknown> & None) | (Eq<unknown> & Ord<unknown> & Some<unknown>)'.
Type 'None' is not assignable to type '(Eq<unknown> & Ord<unknown> & None) | (Eq<unknown> & Ord<unknown> & Some<unknown>)'.
Type 'None' is not assignable to type 'Eq<unknown> & Ord<unknown> & None'.
Type 'None' is not assignable to type 'Eq<unknown>'.
Type 'URItoKind<E>[F]' is not assignable to type 'Eq<unknown> & Ord<unknown> & Some<unknown>'.
Type 'None | Eq<E> | Ord<E> | Some<E>' is not assignable to type 'Eq<unknown> & Ord<unknown> & Some<unknown>'.
Type 'None' is not assignable to type 'Eq<unknown> & Ord<unknown> & Some<unknown>'.
Property 'equals' is missing in type 'None' but required in type 'Eq<unknown>'.
Type 'URItoKind<E>[F]' is not assignable to type 'Eq<unknown>'.
Type 'None | Eq<E> | Ord<E> | Some<E>' is not assignable to type 'Eq<unknown>'.
Property 'equals' is missing in type 'None' but required in type 'Eq<unknown>'.
34 return liftExt(x.runExt(f))
Using the Either type with the Do notation is marked as deprecated with the message "Use small, specific instances instead."
Do(E.either) // <-- `either` is marked deprecated
.bind("name", value)
.return(({name}) => ({out: name}));
It has no compiler warnings or errors.
See above
Should either
really be deprecated, or is there an alternate "smaller" type that we can use in these places?
n/a
Which versions of fp-ts-contrib are affected by this issue? Did this work in previous versions of fp-ts-contrib?
Software | Version(s) |
---|---|
fp-ts | 2.5.3 |
fp-ts-contrib | 0.1.26 |
TypeScript | 3.9.6 (build) and 4.3.2 (editor) |
fp-ts bindings for the inbuilt Iterable<A>
type, which is basically lazy lists.
Software | Version(s) |
---|---|
fp-ts | |
fp-ts-contrib | |
TypeScript |
Currently TaskOption implements Alt1.
Add zero
to TaskOption to implement Alternative1.
const zero = getOptionM(..).none
People using Applicative interface.
Software | Version(s) |
---|---|
fp-ts | 2.0.1 |
fp-ts-contrib | 0.1.14 |
TypeScript | 3.6 |
This is the follow up of gcanti/fp-ts#688
Do
.Do
.Do
's bind, bindL, do, doL, sequenceS, return
methods.import { URIS, Kind } from 'fp-ts/lib/HKT'
import { createInterface } from 'readline'
import { flow, constVoid } from 'fp-ts/lib/function'
import * as C from 'fp-ts/lib/Console'
import * as M from 'fp-ts/lib/Monad'
import * as O from 'fp-ts/lib/Option'
import * as R from 'fp-ts/lib/Random'
import * as T from 'fp-ts/lib/Task'
import { Do } from 'fp-ts-contrib/lib/Do'
// type classes
interface Program<F extends URIS> extends M.Monad1<F> {
finish: <A>(a: A) => Kind<F, A>
}
interface Console<F extends URIS> {
putStrLn: (message: string) => Kind<F, void>
getStrLn: Kind<F, string>
}
interface Random<F extends URIS> {
nextInt: (upper: number) => Kind<F, number>
}
interface Main<F extends URIS> extends Program<F>, Console<F>, Random<F> {}
// instances
const programTask: Program<T.URI> = {
...T.task,
finish: T.of,
}
/**
* read from standard input
*/
const getStrLn: T.Task<string> = () =>
new Promise(resolve => {
const rl = createInterface({
input: process.stdin,
output: process.stdout,
})
rl.question('> ', answer => {
rl.close()
resolve(answer)
})
})
/**
* write to standard output
*/
const putStrLn = flow(C.log, T.fromIO)
const consoleTask: Console<T.URI> = {
getStrLn,
putStrLn,
}
const randomTask: Random<T.URI> = {
nextInt: upper => T.fromIO(R.randomInt(1, upper)),
}
// game
/**
* parse a string to an integer
*/
function parse(s: string): O.Option<number> {
const i = +s
return isNaN(i) || i % 1 !== 0 ? O.none : O.some(i)
}
function main<F extends URIS>(F: Main<F>): Kind<F, void> {
// ask something and get the answer
const ask = (question: string): Kind<F, string> =>
Do(F)
.do(F.putStrLn(question))
.bind('answer', F.getStrLn)
.return(({ answer }) => answer)
const shouldContinue = (name: string): Kind<F, boolean> =>
Do(F)
.bind('answer', ask(`Do you want to continue, ${name} (y/n)?`))
.bindL('result', ({ answer }) => {
switch (answer.toLowerCase()) {
case 'y':
return F.of(true)
case 'n':
return F.of(false)
default:
return shouldContinue(name)
}
})
.return(({ result }) => result)
const gameLoop = (name: string): Kind<F, void> =>
Do(F)
// run `n` tasks in parallel
.sequenceS({
guess: ask(`Dear ${name}, please guess a number from 1 to 5`),
secret: F.nextInt(5),
})
.doL(({ guess, secret }) =>
O.fold(
() => F.putStrLn('You did not enter an integer!'),
(x: number) =>
x === secret
? F.putStrLn(`You guessed right, ${name}!`)
: F.putStrLn(
`You guessed wrong, ${name}! The number was: ${secret}`,
),
)(parse(guess)),
)
.bind('shouldContinue', shouldContinue(name))
.bindL('result', ({ shouldContinue }) =>
shouldContinue ? gameLoop(name) : F.of<void>(undefined),
)
.return(({ result }) => result)
return Do(F)
.bind('name', ask('What is your name?'))
.doL(({ name }) => F.putStrLn(`Hello, ${name} welcome to the game!`))
.doL(({ name }) => gameLoop(name))
.return(constVoid)
}
export const mainTask = main({
...programTask,
...consoleTask,
...randomTask,
})
mainTask()
Thank you.
const foo = Do(TE.taskEither)
.bind("te1", TE.right<boolean, number>(1))
.bindL("te2", () => TE.right<string, string>("wat"));
fails with:
Type 'TaskEither<string, string>' is not assignable to type 'TaskEither<boolean, string>'.
Type 'string' is not assignable to type 'boolean'.ts(2322)
const foo = Do(TE.taskEither)
.bind("te1", TE.right<boolean, number>(1))
.bindLW("te2", () => TE.right<string, string>("wat"))
.return(identity);
Where foo
would be TaskEither<boolean | string, { te1: number} & { te2: string }>
.
π€·ββοΈ Do
is a complex beast.
This would alleviate the need to lift the first bind
's Left
into a union of all possible error types, which I believe is the current accepted workaround. This was the case for Either
's chain
too, until chainW
was added recently. It would be great if we could get the same for Do
.
Would something like this be useful?
Pattern matching is done using if/switch statements in conjunction with type guards.
It would be nice to have a more functional/concise syntax that still provides type safety and exhaustiveness checking (of course).
The following solution was inspired by Peter Fillo's βPattern matchingβ with Typescript done right.
If there is interest, I can create a pull request.
export const Tag = '_tag' as const
interface TaggedType {
[Tag]: string | number | symbol
}
type TypeMap<T extends TaggedType> = {
[K in T[typeof Tag]]: T extends { [Tag]: K } ? T : never
}
type PatternMap<M, T> = {
[K in keyof M]: (e: M[K]) => T
}
export type Pattern<T extends TaggedType, U> = PatternMap<TypeMap<T>, U>
export function match<T extends TaggedType, U>(p: Pattern<T, U>): (e: T) => U {
return (v) => p[v[Tag]](v)
}
It can be used as follows:
import * as E from 'fp-ts/lib/Either'
export const f = match<E.Either<boolean, number>, string>({
Left: (e) => `L: ${e.left}`,
Right: (e) => `R: ${e.right}`,
})
console.log(f(E.right(42))) // "R: 42"
The benefits become more obvious if there are more cases, the above is just for illustration.
All users?
Just using if/switch statements in conjunction with type guards.
Software | Version(s) |
---|---|
fp-ts | 2.6.2 |
fp-ts-contrib | n/a |
TypeScript | 3.9.2 |
I want to declare a variable in Do
.bindL("a", s => m.of(x))
.letL("a", s => x)
In the Slack channel (FP Slack) very often people ask questions (from begginer to advanced level). For the more elaborate ones @gcanti (and others) usually share snippets with answers. Which are very useful and could help people in the future.
But Slack free plan means losing the history of the chat. Regardless of that, a chat application is not the best place to search for snippets.
My suggestion is to add a section in the docs with a curated list of those snippets. Maybe the question could be included as well.
The goal is to keep a record of all the "how do you do X in fp-ts (and friends)?"
Here's and example question and the answer.
Question:
Given
Array<IOEither<E, A>>
. Count how many lefts and rights. Result should beIO<{ success: number, failure: number }>
(orIO<[number, number]>
).
Answer:
use
wilt
(from Witherable)
import { array } from 'fp-ts/lib/Array'
import { identity } from 'fp-ts/lib/function'
import { io } from 'fp-ts/lib/IO'
import { IOEither } from 'fp-ts/lib/IOEither'
declare const xs: Array<IOEither<Error, number>>
// result: IO<Separated<Error[], number[]>>
const result = array.wilt(io)(xs, identity)
Make batchTraverse
pipe-able.
Last invocation of batchTraverse
requires the data (first) and the function (last):
import * as TE from 'fp-ts/TaskEither'
import { batchTraverse } from 'fp-ts-contrib/batchTraverse'
pipe(
/** ... */
(ass) => batchTraverse(TE.taskEither)(ass, f),
/** ... */
)
batchTravers
should be pipe-able with data-last:
import * as TE from 'fp-ts/TaskEither'
import { batchTraverse } from 'fp-ts-contrib/batchTraverse'
pipe(
/** ... */
batchTraverse(TE.taskEither)(f),
/** ... */
)
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.