Skip to content
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

Funfix integration #252

Closed
gcanti opened this issue Oct 15, 2017 · 14 comments
Closed

Funfix integration #252

gcanti opened this issue Oct 15, 2017 · 14 comments

Comments

@gcanti
Copy link
Owner

gcanti commented Oct 15, 2017

(follow up of #251 (comment))

@sledorze a basic integration looks possible

import { Monad } from 'fp-ts/lib/Monad'
import { IO } from 'funfix-effect'

declare module 'fp-ts/lib/HKT' {
  interface URI2HKT<A> {
    'funfix/IO': IO<A>
  }
}

export const URI = 'funfix/IO'

export type URI = typeof URI

export const map = <A, B>(f: (a: A) => B, fa: IO<A>): IO<B> => fa.map(f)

export const of = <A>(a: A): IO<A> => IO.pure(a)

export const ap = <A, B>(fab: IO<(a: A) => B>, fa: IO<A>): IO<B> => fab.chain(f => fa.map(f))

export const chain = <A, B>(f: (a: A) => IO<B>, fa: IO<A>): IO<B> => fa.chain(f)

export const io = ({
  URI,
  map,
  of,
  ap,
  chain
} as any) as Monad<URI>

import { lift } from 'fp-ts/lib/Functor'

const double = (n: number): number => n * 2

const doubleIO = lift(io)(double)

console.log(
  doubleIO(IO.of(() => 1))
    .run()
    .toPromise()
    .then(x => console.log(x))
)
// 2

import { when } from 'fp-ts/lib/Applicative'

when(io)(true)(IO.of(() => console.log('hello'))).run()
// 'hello'

Not sure why I can't define a module augmentation though

// adding this raises an error
declare module 'funfix-effect' {
  interface IO<A> {
    _A: A
    _URI: URI
  }
}
@sledorze
Copy link
Collaborator

@gcanti is that tested against the master or next branch ?

@gcanti
Copy link
Owner Author

gcanti commented Oct 16, 2017

@gcanti
Copy link
Owner Author

gcanti commented Oct 16, 2017

Not sure why I can't define a module augmentation though

@alexandru maybe we could agree on the shape of HK/HKT, what do you think?

// fp-ts
export interface HKT<URI, A> {
  readonly _URI: URI
  readonly _A: A
}

// funfix
export interface HK<F, A> {
  /** Trick for achieving nominal typing. */
  readonly _funKindF: F

  /** Trick for achieving nominal typing. */
  readonly _funKindA: A
}

@alexandru
Copy link

Hello,

Yes, we can agree on an encoding. When I looked at yours a while back, it looked different than mine, but maybe I don't remember correctly.

I don't really like URI as a name, but in this case interop is important.
So I guess I can change it to yours, since mine is a younger project. Is that OK?

I'll also work on Fantasy Land integration if that helps, there's a new issue opened for it.

@gcanti
Copy link
Owner Author

gcanti commented Oct 16, 2017

When I looked at yours a while back, it looked different than mine

@alexandru opss, you are right, _funKindF uses the constructor type, not a unique string literal like in fp-ts, sorry for the confusion.

So the only choice seems to fix that module augmentation issue

@alexandru
Copy link

I don't know right now how that URI works, I'm guessing that it's all type-level stuff and that I don't have to actually have a string in it.

Would this work (without actually initializing those members)?

class IO<A> {
  readonly _URI: 'funfix/IO'
  readonly _F: Option<any>
  readonly _A: A
}

@gcanti
Copy link
Owner Author

gcanti commented Oct 16, 2017

I'm guessing that it's all type-level stuff and that I don't have to actually have a string in it.

Yes

Would this work (without actually initializing those members)?

Yes, just checked changing manually the definition file. However you would be forced to

  • change the field _funKindA to _A (or add a new field _A)
  • add a field (_URI)

If you're not ok with that, I'll try to understand what's going on with module augmentation

I got it! This works

declare module 'funfix-effect/dist/io' {
  interface IO<A> {
    _A: A
    _URI: URI
  }
}

@gcanti
Copy link
Owner Author

gcanti commented Oct 16, 2017

@alexandru @sledorze So these are the steps in order to integrate a funfix data type

import { Future } from 'funfix-exec'

// step 1: choose a unique string literal representing the data type
export const URI = 'funfix/Future'

export type URI = typeof URI

// step 2: module augmentation of the original source
declare module 'funfix-exec/dist/future' {
  interface Future<A> {
    _A: A
    _URI: URI
  }
}

// step 3: HKT module augmentation
declare module 'fp-ts/lib/HKT' {
  interface URI2HKT<A> {
    'funfix/Future': Future<A>
  }
}

// step 4: define type class instances
import { Monad } from 'fp-ts/lib/Monad'

export const map = <A, B>(f: (a: A) => B, fa: Future<A>): Future<B> => fa.map(f)

export const of = <A>(a: A): Future<A> => Future.pure(a)

export const ap = <A, B>(fab: Future<(a: A) => B>, fa: Future<A>): Future<B> => fab.flatMap(f => fa.map(f))

export const chain = <A, B>(f: (a: A) => Future<B>, fa: Future<A>): Future<B> => fa.flatMap(f)

export const future: Monad<URI> = {
  URI,
  map,
  of,
  ap,
  chain
}

Then you can use Future with fp-ts as usual

import { liftA2 } from 'fp-ts/lib/Apply'

const sum = (a: number) => (b: number) => a + b

// sumA2: (a: Future<number>) => (b: Future<number>) => Future<number>
const sumA2 = liftA2(future)(sum)

sumA2(Future.pure(2))(Future.pure(2))
  .toPromise()
  .then(x => console.log(x))
// => 4

That's pretty cool

@SimonMeskens
Copy link
Collaborator

I described a more idiomatic way to specify HKTs in #156. Maybe there's a similar spec we could use to better integrate all possible future libraries?

@alexandru
Copy link

It would be a good idea to have a common protocol for higher kinds actually.
Will comment on that issue.

@sledorze
Copy link
Collaborator

@alexandru @gcanti @SimonMeskens Yes! let's pave the road for 'type level' quality libs in typescript land :)

@sledorze
Copy link
Collaborator

Several things on that front:

One is really the interop of the libraries because of base types: like Eithers not being the same.
io-ts(based on fp-ts) for instance is really useful and use fp-ts Either but funfix attempt is also really useful dans both rely on non compatible base types.

https://github.com/gcanti/io-ts/blob/master/src/index.ts#L20
https://funfix.org/api/effect/classes/io.html#attempt

So we need a conversion layer to switch back and forth if the types are differents.
The other possibility is to share the types, to be exhaustive on that analysis, funfix want to address both typescript and flow, which is a show stopper for the use of fp-ts (or needs to reimplement fp-ts for flow which lead to more discussion and work..), on the other side, fp-ts goes maybe farer than funfix on type level stuff in typescript, so using funfix base types may not be an option.
Another solution would be to abstract on those types in funfix, HKT everywhere, but we're not using a language that helps on that matter (no implicit dict, no implicits) and it touches everything, lead to perf issues etc..

Maybe interrop between those libs is too much work and a non goal and that issue can be closed.

@gcanti @alexandru what do you think?

@alexandru
Copy link

@gcanti I am experimenting with your encoding for HKT and might use it as well.

I'm guessing that the current shape is fairly stable, right?

export interface HKT<URI, A> {
  readonly _URI: URI
  readonly _A: A
}

@gcanti
Copy link
Owner Author

gcanti commented Nov 2, 2017

I'm guessing that the current shape is fairly stable, right?

@alexandru Yes, it is

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants