Corecursion with State

State is beneficial on corecursion; we can rewrite our old examples with State:

fun <T, S> unfold(s: S, f: (S) -> Pair<T, S>?): Sequence<T> {
val result = f(s)
return if (result != null) {
sequenceOf(result.first) + unfold(result.second, f)
} else {
sequenceOf()
}
}

Our original unfold function use a function, f: (S) -> Pair<T,S>? which is very similar to State<S, T>:

fun <T, S> unfold(s: S, state: State<S, Option<T>>): Sequence<T> {
val (actualState: S, value: Option<T>) = state.run(s)
return value.fold(
{ sequenceOf() },
{ t ->
sequenceOf(t) + unfold(actualState, state)
})
}

Instead of having a lambda (S) -> Pair<T, S>?, we use State<S, Option<T>> and we use the function fold from Option, with an empty Sequence for None or a recursive call for Some<T>:

fun factorial(size: Int): Sequence<Long> {
return sequenceOf(1L) + unfold(1L to 1) { (acc, n) ->
if (size > n) {
val x = n * acc
(x) to (x to n + 1)
} else
null
}
}

Our old factorial function uses unfold with Pair<Long, Int> and a lambda—(Pair<Long, Int>) -> Pair<Long, Pair<Long, Int>>?:

import arrow.syntax.option.some

fun factorial(size: Int): Sequence<Long> {
return sequenceOf(1L) + unfold(1L toT 1, State { (acc, n) ->
if (size > n) {
val x = n * acc
(x toT n + 1) toT x.some()
} else {
(0L toT 0) toT None
}
})
}

The refactored factorial uses State<Tuple<Long, Int>, Option<Long>> but internal logic is almost the same, although our new factorial doesn't use null, which is a significant improvement:

fun fib(size: Int): Sequence<Long> {
return sequenceOf(1L) + unfold(Triple(0L, 1L, 1)) { (cur, next, n) ->
if (size > n) {
val x = cur + next
(x) to Triple(next, x, n + 1)
}
else
null
}
}

Similarly, fib uses unfold with Triple<Long, Long, Int> and a lambda (Triple<Long, Long. Int>) -> Pair<Long, Triple<Long, Long, Int>>?:

import arrow.syntax.tuples.plus

fun fib(size: Int): Sequence<Long> {
return sequenceOf(1L) + unfold((0L toT 1L) + 1, State { (cur, next, n) ->
if (size > n) {
val x = cur + next
((next toT x) + (n + 1)) toT x.some()
} else {
((0L toT 0L) + 0) toT None
}
})
}

And the refactored fib uses State<Tuple3<Long, Long, Int>, Option<Long>>. Pay close attention to the extension operator function plus, used with Tuple2<A, B> and C will return Tuple3<A, B, C>:

fun main(args: Array<String>) {
factorial(10).forEach(::println)
fib(10).forEach(::println)
}

And now, we can use our corecursive functions to generate sequences. There are many other uses for State that we can't cover here, such as Message History from Enterprise Integration Patterns (http://www.enterpriseintegrationpatterns.com/patterns/messaging/MessageHistory.html) or navigation on forms with multiple steps such as plane checking or long registration forms.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.93.0