This is an experimental implementation of temporal-logic-based Hindley-Milner type system.
This is (currently) not for practical use.
In NML, technically, there are three temporal modal operators: Next(○), Finally(◇), and Globally(□).
The basic idea of NML's type system is that every non-temporal type is □-type.
This restriction makes it able to explicitly insert run when we want to "lift" ("fmap", or whatever) a function and then apply it to a delayed (quoted) term.
(@ 1 @) has type Next[1] Nat, which means that we can obtain a Nat value at the next stage.
Also, <@ 1 @> has type Finally Nat, which means that we can obtain a Nat value at some stage in the future. So, Finally Nat essentially means Next[inf] Nat.
let-next x = (@ 1 @) in
if eq x 0 then
true
else
false
is the equivalent of
let x = (@ 1 @) in
(@
if eq (run[1] x) 0 then
true
else
false
@)
, delays the computation, and thus has type Next[1] Bool.
Similarly,
let-finally x = <@ 1 @> in x + 1
is the equivalent of
let x = <@ 1 @> in <@ (run[inf] x) + 1 @>
and has type Finally Nat.
The classical "scan" function can be thought as a function that returns a delayed value.
> readNat;;
type: Unit -> Next[1] Nat
Now we can use this like:
let-next x = readNat () in
let-next y = readNat () in
let-next z = readNat () in
(x + y) * z
In NML, there is a built-in subtyping relation based on stage:
a <: Next[1] a <: Next[2] a <: ... <: Next[inf] a = Finally a
This means that, for example, a function with type Finally a -> b can also be applied to a value with type Next[n] a for any n, including 0 (= a).
See further example below:

The execution of a NML program is defined as
eval :: Next[n] a -> a (for any n in [0,inf])
eval program = run[inf] program
Each computation in each stage is kept pure functional:
This ensures the linearity of the whole execution, while
RealWorld in GHC)Also, it should be doable to express parallel computation in this type system as
parallel :: List (Finally a) -> Finally (List a)
, and I'm currently investigating it.
fixpoint)match)
function)let (+) l r = ...)Next[1] T)
(@ t @))let-next)macro)Finally T, <@ t @>, let-finally)
GPL v3
F#
98.5%
This is an experimental implementation of temporal-logic-based Hindley-Milner type system.
This is (currently) not for practical use.
In NML, technically, there are three temporal modal operators: Next(○), Finally(◇), and Globally(□).
The basic idea of NML's type system is that every non-temporal type is □-type.
This restriction makes it able to explicitly insert run when we want to "lift" ("fmap", or whatever) a function and then apply it to a delayed (quoted) term.
(@ 1 @) has type Next[1] Nat, which means that we can obtain a Nat value at the next stage.
Also, <@ 1 @> has type Finally Nat, which means that we can obtain a Nat value at some stage in the future. So, Finally Nat essentially means Next[inf] Nat.
let-next x = (@ 1 @) in
if eq x 0 then
true
else
false
is the equivalent of
let x = (@ 1 @) in
(@
if eq (run[1] x) 0 then
true
else
false
@)
, delays the computation, and thus has type Next[1] Bool.
Similarly,
let-finally x = <@ 1 @> in x + 1
is the equivalent of
let x = <@ 1 @> in <@ (run[inf] x) + 1 @>
and has type Finally Nat.
The classical "scan" function can be thought as a function that returns a delayed value.
> readNat;;
type: Unit -> Next[1] Nat
Now we can use this like:
let-next x = readNat () in
let-next y = readNat () in
let-next z = readNat () in
(x + y) * z
In NML, there is a built-in subtyping relation based on stage:
a <: Next[1] a <: Next[2] a <: ... <: Next[inf] a = Finally a
This means that, for example, a function with type Finally a -> b can also be applied to a value with type Next[n] a for any n, including 0 (= a).
See further example below:

The execution of a NML program is defined as
eval :: Next[n] a -> a (for any n in [0,inf])
eval program = run[inf] program
Each computation in each stage is kept pure functional:
This ensures the linearity of the whole execution, while
RealWorld in GHC)Also, it should be doable to express parallel computation in this type system as
parallel :: List (Finally a) -> Finally (List a)
, and I'm currently investigating it.
fixpoint)match)
function)let (+) l r = ...)Next[1] T)
(@ t @))let-next)macro)Finally T, <@ t @>, let-finally)
GPL v3
F#
98.5%