interface Functor<A>
A
A
from Functor<A>
, nor to create a new Functor<A>
based on the existing value of type A
But even with such restrictions, the functor is useful - if for some type B
we can convert A
to B
(in other words, there is a function (a: A) -> B
), then we can write a function (f: Functor<A>) -> Functor<B>
and name its map
: interface Functor<A> { fun <B> map(f: (A) -> B): Functor<B> }
map((a) -> a)
must return the same functormap((a) -> f(a)).map((b) -> g(b))
must be identical to map(a -> g(f(a))
// - , , map data class MyRandom<A>( val get: (bits: Int) -> A ) { companion object { val intRandom: MyRandom<Int> = MyRandom { Random.nextBits(it) } val hexRandom: MyRandom<String> = intRandom.map { it.toString(16) } } } // map fun <A, B> MyRandom<A>.map(f: (A) -> B): MyRandom<B> = MyRandom(get = {bits -> f(get(bits)) }) fun main(args: Array<String>) { println("random=" + MyRandom.intRandom.get(12)) // random=1247 println("hexRandom=" + MyRandom.hexRandom.get(12)) // hexRandom=c25 }
map
List<A>
MyInputStream<A>
Optional<A>
lift
: fun <A> lift(value: A): Monad<A> = TODO()
flatMap
, it is more complicated, so first we give our entire monad interface: interface Monad<A> { // , map - // flatMap lift fun <B> map(f: (A) -> B): Monad<B> = flatMap { a -> lift(f(a)) } fun <B> flatMap(f: (A) -> Monad<B>): Monad<B> } fun <A> lift(value: A): Monad<A> = TODO()
// , Int // - // val readInt: Monad<Int> = TODO() // , - fun readBytes(len: Int): Monad<ByteArray> = TODO() // , , val bytes: Monad<ByteArray> = readInt.flatMap {len -> if (len > 0) readBytes(len) // - else lift(ByteArray(0)) // , }
data class Option<A>(val value: A?) { fun <B> map(f: (A) -> B): Option<B> = flatMap { a -> lift(f(a)) } fun <B> flatMap(f: (A) -> Option<B>): Option<B> = when(value) { null -> Option(null) else -> f(value) } } fun <A> lift(value: A?): Option<A> = Option(value) fun mul(a: Option<Int>, b: Option<Int>): Option<Int> = a.flatMap { a -> b.map { b -> a * b } } fun main(args: Array<String>) { println(mul(Option(4), Option(5)).value) // 20 println(mul(Option(null), Option(5)).value) // null println(mul(Option(4), Option(null)).value) // null println(mul(Option(null), Option(null)).value) // null }
data class DB<A>(val f: (Connection) -> A) { fun <B> map(f: (A) -> B): DB<B> = flatMap { a -> lift(f(a)) } fun <B> flatMap(f: (A) -> DB<B>): DB<B> = DB { conn -> f(this.f(conn)).f(conn) } } fun <A> lift(value: A): DB<A> = DB { value } fun select(id: Int): DB<String> = DB { conn -> val st = conn.createStatement() // .... TODO() } fun update(value: String): DB<Unit> = DB { conn -> val st = conn.createStatement() // .... TODO() } fun selectThenUpdate(id: Int): DB<Unit> = select(id).flatMap { value -> update(value) } fun executeTransaction(c: Connection): Unit { // , // val body: DB<Unit> = selectThenUpdate(42) // , select update body.f(c) c.commit() }
data class Option<A>(value: A?)
data class Either<Error, A>(value: Pair<Error?, A?>)
data class Defer<A>(value: () -> A)
java.util.concurrent.CompletableFuture<A>
data class State<S, A>(value: (S) -> Pair<S, A>)
Source: https://habr.com/ru/post/454534/
All Articles