scalabook
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) + 1val times2 = (_: Int) * 2val 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) + 1val f2 = (_:Int) * 100
(f1 >>> f2)(2) // 300(f1 <<< f2)(2) // 201
Ссылки: