As it stands, func
doesn't type check because we can't construct the infinite type t a ~ (a -> b) -> t b
. However, we can get around that restriction by creating a new data type as follows.
// Chain :: (forall b. (a -> b) -> Chain b) -> Chain a
const Chain = chain => ({ chain });
// func :: a -> Chain a
const func = x => Chain(k => func(k(x)));
// add :: Number -> Number -> Number
const add = x => y => x + y;
// trace :: a -> a
const trace = x => {
console.log(x);
return x;
};
// Chain Number
func(1).chain(add(2)).chain(add(2)).chain(trace);
Now, Chain
is a very weird data type. It's an infinite chain of type-aligned functions.
Chain a
= (a -> b) -> Chain b
= (a -> b) -> (b -> c) -> Chain c
= (a -> b) -> (b -> c) -> (c -> d) -> Chain d
-- ad infinitum
However, this data type is not very useful for purely functional programming because you can never consume a Chain
value. The only way you can make use of intermediate results in the chain is by using an impure function like trace
. I'd suggest that you avoid such useless data types altogether.