Arrow
Формальное определение
Arrow
("стрелка") — это термин, используемый в теории категорий как абстрактное понятие вещи,
которая ведет себя как функция. "Стрелки", могут быть полезны, если нужно добавить некоторый контекст к функциям и парам.
Каждая "стрелка" образует Contravariant
в одном параметре типа и Applicative
в другом, как и в случае с обычными функциями.
Arrow
расширяет Split
, Strong
и Category
.
Arrow
должен удовлетворять законам расширяемых трейтов.
Определение в виде кода на Scala
trait Arrow[=>:[_, _]] extends Split[=>:], Strong[=>:], Category[=>:]:
self =>
/** 'Поднятие' обычной функции до Arrow. */
def arr[A, B](f: A => B): A =>: B
/** Псевдоним для `compose`. */
final def <<<[A, B, C](fbc: B =>: C, fab: A =>: B): A =>: C =
compose(fbc, fab)
/** Обратный `<<<`. */
final def >>>[A, B, C](fab: A =>: B, fbc: B =>: C): A =>: C =
compose(fbc, fab)
/** Меняет пару местами */
def swap[X, Y]: (X, Y) =>: (Y, X) = arr[(X, Y), (Y, X)] { case (x, y) => (y, x) }
/** Пропустить `C` нетронутым. */
override def second[A, B, C](f: A =>: B): (C, A) =>: (C, B) =
>>>(<<<(first[A, B, C](f), swap), swap)
/** Запустить `fab` и `fcd` рядом друг с другом. Иногда обозначается как `***`. */
override def split[A, B, C, D](fab: A =>: B, fcd: C =>: D): (A, C) =>: (B, D) =
>>>(first[A, B, C](fab), second[C, D, B](fcd))
/** Запустить `fab` и `fac` на одном и том же `A`. Иногда обозначается как `&&&`. */
def combine[A, B, C](fab: A =>: B, fac: A =>: C): A =>: (B, C) =
>>>(arr((a: A) => (a, a)), split(fab, fac))
/** Contramap on `A`. */
override def mapfst[A, B, C](fab: A =>: B)(f: C => A): C =>: B =
>>>[C, A, B](arr(f), fab)
/** Functor map on `B`. */
override def mapsnd[A, B, C](fab: A =>: B)(f: B => C): A =>: C =
<<<[A, B, C](arr(f), fab)
override def covariantInstance[C]: Applicative[[X] =>> =>:[C, X]] =
new Applicative[[X] =>> =>:[C, X]]:
override def unit[A](a: => A): C =>: A = arr(_ => a)
override def apply[A, B](fab: C =>: (A => B))(fa: C =>: A): C =>: B =
<<<(arr((y: (A => B, A)) => y._1(y._2)), combine(fab, fa))
/** Запустить два `fab` рядом друг с другом */
def product[A, B](fab: A =>: B): (A, A) =>: (B, B) =
split(fab, fab)
Примеры
Функция от одной переменной
given Arrow[Function1] with
override def arr[A, B](f: A => B): A => B = f
override def id[A]: A => A = a => a
override def compose[A, B, C](f: B => C, g: A => B): A => C = g andThen f
override def first[A, B, C](fa: A => B): ((A, C)) => (B, C) = (a, c) => (fa(a), c)
Реализация
Реализация в ScalaZ
import scalaz.*
import Scalaz.*
val plus1 = (_: Int) + 1
val times2 = (_: Int) * 2
val rev = (_: String).reverse
plus1.first apply (7 -> "abc") // (8,abc)
plus1.second apply ("def" -> 14) // (def,15)
plus1 *** rev apply (7 -> "abc") // (8,cba)
plus1 &&& times2 apply 7 // (8,14)
plus1.product apply (9 -> 99) // (10,100)
val f1 = (_:Int) + 1
val f2 = (_:Int) * 100
(f1 >>> f2)(2) // 300
(f1 <<< f2)(2) // 201
Ссылки: