2

I can't seem to find the correct typing for an infinite list in ReScript.

I first tried:

type rec stream<'a> = ('a, () => ('a, stream<'a>))

Which was incorrect because ReScript thought the type was cyclic. So instead I tried:

type rec stream<'a> = ('a, Lazy.t<() => ('a, stream<'a>)>)

Which still gave me a type error.

Ultimately I am trying to get this piece of code to work but it fails due to the type signature being infinite.

let rec from: (int, Lazy.t<() => (int, Lazy.t<...>) = (x: int) => {
    (x, () => Lazy.from_fun(from(x+1)))
}
glennsl
  • 28,186
  • 12
  • 57
  • 75
Ryan Schaefer
  • 3,047
  • 1
  • 26
  • 46
  • You can reuse OCaml's Seq, which is an infinite stream: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Seq.html –it doesn't have any native dependencies, so you should just be able to copy and paste it directly into your project. – Yawar Jan 13 '21 at 00:19
  • Yeah, I could do it with a variant and some way of ending the list, I was just trying to avoid that – Ryan Schaefer Jan 13 '21 at 01:01
  • There's a `-rectypes` flag you can pass the compiler which should have it accept this. – glennsl Jan 13 '21 at 06:19
  • To provide more context as to why I was avoiding it as well. I want someone to **always** be able to get a `next`, even if the "end" occurs because `next` keeps returning `None`. Therefore, the type itself defines that there will always be a next rather than pretending / hoping there always is. I hope this makes sense. – Ryan Schaefer Jan 13 '21 at 15:20

1 Answers1

3

If your only objection to Sequence is the Nil case, you can define

type rec stream<'a> = { x:'a, next:() => stream<'a> }

The difference with your definition is that we are creating a new recursive type, rather than trying to define a recursive type expression.

EDIT:

The difference between using a tuple or a record is that tuples are structurally typed whereas records are nominally typed.

This changes everything in term of equality and identity.

In particular the original definition can be read as

type stream0<'a> = ('a, () => 'b ) as 'b

Then whenever one wants to compare a type stream0<'a> with a tuple type, it might be needed to expand this abbreviation an unbounded number of time.

For instance, this function is well-typed.

let f: stream0<int> => (int, ()=>(int,() => _)) = (x) => x

or this one:

let app_once = ((x,next)) => next ()

One important consequence here is that it is possible to use a stream0<'a> in a context where a finite stream was expected. In other words, there are many have many potential equality between a stream0<'a> and a finite version of stream0<'a>.

Contrarily, the record definition of stream<'a> create a new and distinct type constructor stream. Consequently, a stream<'a> can only ever be equal to stream<'a>. In other words, a function designed for a stream<'a>

let app_once  = {x;next} => next(())

cannot work with the type

type one_stream<'a> = { x:'a, next:() => ('a, ()) }

In practice, the recursive type of stream0<'a> and its many type equalities is more bothersome than useful, and such recursive types are disabled by default.

octachron
  • 17,178
  • 2
  • 16
  • 23