### Giving

[
  Entity given-value
  Funcon initialise-giving
  Funcon give
  Funcon given
  Funcon no-given
  Funcon left-to-right-map
  Funcon interleave-map
  Funcon left-to-right-repeat
  Funcon interleave-repeat
  Funcon left-to-right-filter
  Funcon interleave-filter
  Funcon fold-left
  Funcon fold-right
]


Meta-variables
  T, T' <: values
  T? <: values?


Entity
  given-value(_:values?) |- _ ---> _
/*
  The given-value entity allows a computation to refer to a single
  previously-computed `V:values`. The given value `( )` represents 
  the absence of a current given value.
*/


Funcon
  initialise-giving(X:( )=>T') : ( )=>T'
   ~> no-given(X)
/*
  `initialise-giving(X)` ensures that the entities used by the funcons for
  giving are properly initialised.
*/


Funcon
  give(_:T, _:T=>T') : =>T'
/*
  `give(X, Y)` executes `X`, possibly referring to the current `given` value,
  to compute a value `V`. It then executes `Y` with `V` as the `given` value,
  to compute the result.
*/
Rule
  given-value(V) |- Y ---> Y'
  ------------------------------------------------
  given-value(_?) |- give(V:T, Y) ---> give(V, Y')
Rule
  give(_:T, W:T') ~> W


Funcon
  given : T=>T
/*
  `given` refers to the current given value.
*/
Rule
  given-value(V:values) |- given ---> V
Rule
  given-value( ) |- given ---> fail


Funcon
  no-given(_:( )=>T') : ( )=>T'
/*
  `no-given(X)` computes `X` without references to the current given value.
*/
Rule
         given-value( ) |- X ---> X'
  ------------------------------------------------
  given-value(_?) |- no-given(X) ---> no-given(X')
Rule
  no-given(U:T') ~> U


#### Mapping

/*
  Maps on collection values can be expressed directly, e.g.,
  `list(left-to-right-map(F, list-elements(L)))`.
*/

Funcon
  left-to-right-map(_:T=>T', _:(T)*) : =>(T')*
/*
  `left-to-right-map(F, V*)` computes `F` for each value in `V*` from left
  to right, returning the sequence of resulting values.
*/

Rule
  left-to-right-map(F, V:T, V*:(T)*)
    ~> left-to-right(give(V, F), left-to-right-map(F, V*))
Rule
  left-to-right-map(_, ( )) ~> ( )


Funcon
  interleave-map(_:T=>T', _:(T)*) : =>(T')*
/*
  `interleave-map(F, V*)` computes `F` for each value in `V*` interleaved, 
  returning the sequence of resulting values.
*/
Rule
  interleave-map(F, V:T, V*:(T)*)
    ~> interleave(give(V, F), interleave-map(F, V*))
Rule
  interleave-map(_, ( )) ~> ( )


Funcon
  left-to-right-repeat(_:integers=>T', _:integers, _:integers) : =>(T')*
/*
  `left-to-right-repeat(F, M, N)` computes `F` for each value from `M` to `N` 
  sequentially, returning the sequence of resulting values.
*/
Rule
  is-less-or-equal(M, N) == true
  -------------------------------------------------------------------------
  left-to-right-repeat(F, M:integers, N:integers)
    ~> left-to-right(give(M, F), left-to-right-repeat(F, int-add(M, 1), N))
Rule
  is-less-or-equal(M, N) == false
  ----------------------------------------------
  left-to-right-repeat(_, M:integers, N:integers) ~> ( )


Funcon
  interleave-repeat(_:integers=>T', _:integers, _:integers) : =>(T')*
/*
  `interleave-repeat(F, M, N)` computes `F` for each value from `M` to `N` 
  interleaved, returning the sequence of resulting values.
*/
Rule
  is-less-or-equal(M, N) == true
  -------------------------------------------------------------------
  interleave-repeat(F, M:integers, N:integers)
    ~> interleave(give(M, F), interleave-repeat(F, int-add(M, 1), N))
Rule
  is-less-or-equal(M, N) == false
  -------------------------------------------
  interleave-repeat(_, M:integers, N:integers) ~> ( )


#### Filtering

/*
 Filters on collections of values can be expressed directly, e.g., 
 `list(left-to-right-filter(P, list-elements(L)))` to filter a list `L`.
*/


Funcon
  left-to-right-filter(_:T=>booleans, _:(T)*) : =>(T)*
/*
  `left-to-right-filter(P, V*)` computes `P` for each value in `V*` from left
  to right, returning the sequence of argument values for which the result is
  `true`.
*/
Rule
  left-to-right-filter(P, V:T, V*:(T)*)
   ~> left-to-right(when-true(give(V, P), V), left-to-right-filter(P, V*))
Rule
  left-to-right-filter(_) ~> ( )


Funcon
  interleave-filter(_:T=>booleans, _:(T)*) : =>(T)*
/*
  `interleave-filter(P, V*)` computes `P` for each value in `V*` interleaved,
  returning the sequence of argument values for which the result is `true`.
*/
Rule
  interleave-filter(P, V:T, V*:(T)*)
   ~> interleave(when-true(give(V, P), V), interleave-filter(P, V*))
Rule
  interleave-filter(_) ~> ( )


#### Folding

Funcon
  fold-left(_:tuples(T,T')=>T, _:T, _:(T')*) : =>T
/*
  `fold-left(F, A, V*)` reduces a sequence `V*` to a single value by folding it
  from the left, using `A` as the initial accumulator value, and iteratively
  updating the accumulator by giving `F` the pair of the accumulator value and
  the first of the remaining arguments.
*/
Rule
  fold-left(_, A:T, ( )) ~> A
Rule
  fold-left(F, A:T, V:T', V*:(T')*) ~> fold-left(F, give(tuple(A, V), F), V*)


Funcon
  fold-right(_:tuples(T,T')=>T', _:T', _:(T)*) : =>T'
/*
  `fold-right(F, A, V*)` reduces a sequence `V*` to a single value by folding it
  from the right, using `A` as the initial accumulator value, and iteratively
  updating the accumulator by giving `F` the pair of the the last of the 
  remaining arguments and the accumulator value.
*/
Rule
  fold-right(_, A:T', ( )) ~> A
Rule
  fold-right(F, A:T', V*:(T)*, V:T) ~> give(tuple(V, fold-right(F, A, V*)), F)
