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

Recursive types? #13

Open
m0nkfish opened this issue Aug 19, 2020 · 6 comments
Open

Recursive types? #13

m0nkfish opened this issue Aug 19, 2020 · 6 comments

Comments

@m0nkfish
Copy link

  • I'm submitting a ...
    [ ] bug report
    [x] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary

I'm wondering if it's possible to support recursive ADTs, for example a classic linked list. My own attempts at a generalised ADT library have failed in this regard since the TS compiler can't handle the recursive type aliases

@twop
Copy link
Owner

twop commented Aug 20, 2020

Hey,

Thanks for the request!

it is technically possible to use an interesting trick with const declarations

const Obj = {
  prop: Obj // Error: Block-scoped variable 'Obj' used before its declaration.
}

const Obj2 = {
  prop: ()=> Obj2
}

console.log('hey it works', Obj2.prop())

Example: https://stackblitz.com/edit/recursive-type?file=index.ts

I was thinking about it, but in my own practice I haven't found a need for it.

A potential API:

import {Union, of, Self} from 'ts-union';
const List = Union({
  Empty: of(null),
  Elem: of<number, Self>() // 'number' is a type of values to store
})

Then type Self can be substitute (I'm not sure if it will actually work in ts though).

Or something like that

import {Union, of} from 'ts-union';

const List = Union(a => ({
  Empty: of(null),
  Elem: of(a, ()=>List) // or a wrapper defer(()=>List)
}))

Note that the version above is a generic variant, so it is akin to just classic List.

I'm not sure if this complexity is worth it, but feel free to maybe sketch out a syntax + types to brainstorm :)

@m0nkfish
Copy link
Author

m0nkfish commented Aug 29, 2020

here's a use case for example, to model effects like PromiseAction - i implemented a quick equivalent in this case but would have used ts-union if there were an established way to reference self in types.

the Self idea is very interesting, i like it! i will see if i can come up with a scratchpad...


crude attempt: only replaces one level deep. not sure if possible to recurse within the type??

type Substitute<T, ToReplace, Replacement> =
  T extends ToReplace
  ? Replacement
  : T extends Array<infer U>
  ? Array<Substitute<U, ToReplace, Replacement>>
  : { [K in keyof T] : Substitute<T[K], ToReplace, Replacement> }

type List = 
| { type: 'nil' }
| { type: 'cons', item: number, rest: Self }

type SubbedList = Substitute<List, Self, List>

unfortunately type SubbedList = Substitute<List, Self, SubbedList> returns the usual recursive type error

@m0nkfish
Copy link
Author

m0nkfish commented Sep 9, 2020

I kept plugging away - here's a proof-of-concept with the substitution approach

@twop
Copy link
Owner

twop commented Sep 9, 2020

@m0nkfish that's awesome!
I wonder how matching will work with Self, essentially matching is also recursive. Do you have an API sketch in mind?

@m0nkfish
Copy link
Author

m0nkfish commented Sep 21, 2020

@twop match should be unaffected since Self is substituted in the case creation function. if you want to define a recursive function you can do so:

const List = Union({
  nil: Type<null>(),
  cons: Type<{ head: number, tail: Self }>(),
})
type List = typeof List.T

function sum(list: List): number {
  return List.match(list, {
    nil: () => 0,
    cons: ({ head, tail }) => head + sum(tail)
  })
}

@twop
Copy link
Owner

twop commented Oct 3, 2020

Upd: I didn't forget about this feature request. I'm working on a new feature: matching tuple of unions, and I don't want to focus on a new feature until I ship an MVP of matching tuples.

@m0nkfish thanks again for the patience and working on PoV for recursive types ! <3

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

2 participants