module Data.Late

%default total

------------------------------------------------------------------------
-- Type

public export
data Late : Type -> Type where
  Now : a -> Late a
  Later : Inf (Late a) -> Late a

------------------------------------------------------------------------
-- Creating late values

||| Never return
never : Late a
never = Later never

||| Run a small state machine until it reaches a final state and yields a value.
public export
unfold : (seed -> Either seed value) -> seed -> Late value
unfold f s = case f s of
  Left s' => Later (unfold f s')
  Right v => Now v

||| It's easier to define map and (<*>) in terms of bind so let's start
||| by defining it.
public export
bind : Late a -> (a -> Late b) -> Late b
bind (Now v) f = f v
bind (Later d) f = Later (bind d f)

------------------------------------------------------------------------
-- Inspecting late values

||| Check whether we already have a value.
public export
isNow : Late a -> Maybe a
isNow (Now v) = Just v
isNow (Later d) = Nothing

||| Wait for one tick, hoping to get a value.
public export
wait : Late a -> Late a
wait (Later d) = d
wait d = d

||| Wait for a set number of ticks.
public export
engine : Nat -> Late a -> Late a
engine Z = id
engine (S n) = engine n . wait

||| Wait for a set number of ticks, hoping to get a value.
public export
petrol : Nat -> Late a -> Maybe a
petrol n = isNow . engine n

||| Accelerate makes things happen twice as fast.
public export
accelerate : Late a -> Late a
accelerate (Later (Later d)) = Later (accelerate d)
accelerate (Later (Now v)) = Now v
accelerate d = d

------------------------------------------------------------------------
-- Instances

public export
Functor Late where
  map f d = bind d (Now . f)

public export
Applicative Late where
  pure = Now
  df <*> dx = bind df (\ f => map (f $) dx)

public export
Monad Late where
  (>>=) = bind
