Я играл со Скалазом уже пару выходных. Scalaz привносит в Scala некоторые общие функции и абстракции, которых нет в текущем Scala API. Это в основном * функциональные * структуры более высокого порядка, которые Haskell может похвастать как часть их стандартного распределения. Используя Scalaz, вы можете написать код Scala в аппликативном стиле, который часто оказывается более выразительным, чем императивный.
Функторы, аппликативы, стрелки, монады и многие другие абстракции являются частью репертуара Скалаза. Неудивительно, что использование Scalaz требует от вас нового мышления по сравнению со стандартным Scala. Вам нужно больше думать, как будто вы моделируете в Haskell, а не в каком-либо объектно-ориентированном языке.
Классы типов являются краеугольным камнем дистрибутива Scalaz. Вместо полиморфного мышления в иерархиях наследования подумайте о разработке API для открытого мира с использованием классов типов. В Scalaz реализована иерархия классов типов Haskell — Functors, Pointed, Applicative, Monad и связанные с ними операции.
Чем это отличается от обычного мышления? Давайте рассмотрим пример с текущей точки зрения Scala.
Мы говорим, что с помощью Scala мы можем создавать монадические абстракции. flatMap — это привязка, которая помогает нам склеивать абстракции, как если бы вы делали с >> = Haskell. Но есть ли в стандартной библиотеке Scala абстракция монады? Нет! Если бы у него была монада, мы бы смогли абстрагироваться от нее как от отдельного типа и реализовать API-интерфейсы, такие как sequence, в Haskell.
sequence :: Monad m => [m a] -> m [a]
У нас нет этого в стандартной библиотеке Scala. Рассмотрим другой пример класса типов Applicative, который определен в Haskell как
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
…
Здесь pure поднимает a в эффективную среду функтора, а (<*>) берет функцию из функтора и применяет ее к значениям функтора. Scalaz реализует Applicative в Scala, так что вы можете написать следующее:
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala> List(10, 20, 30) <*> (List(1, 2, 3) map ((_: Int) * (_: Int)).curried)
res14: List[Int] = List(10, 20, 30, 20, 40, 60, 30, 60, 90)
Здесь у нас есть чистая функция, которая умножает 2 Ints. Мы карри функцию и частично применим к членам списка (1, 2, 3). Список заметок является экземпляром Applicative Functor. Затем мы получаем список частичных приложений. Наконец, <*> берет этот Список и применяется к каждому члену Списка (10, 20, 30) как декартово произведение. Конечно, вариант на Haskell гораздо менее многословен.
(*) <$> [1, 2, 3] <*> [10, 20, 30]
и это связано с лучшим выводом типов и карри по умолчанию для стратегии применения функций.
Вы можете получить более сжатый вариант в Scalaz, используя | @ | комбинатор ..
scala> List(10, 20, 30) |@| List(1, 2, 3) apply (_ * _)
res17: List[Int] = List(10, 20, 30, 20, 40, 60, 30, 60, 90)
У вас может быть много экземпляров Аппликативов, если вы выполняете договор в соответствии с приведенным выше определением. Классы типов дают вам возможность определять абстракции для открытого мира. Как и List, в Scalaz реализовано множество других аппликативов, таких как опции, кортежи, приложения функций и т. Д. Прелесть этой реализации в том, что вы можете абстрагироваться над ними единообразным образом с помощью системы типов Scala. Как и в случае со списком, вы можете применить <*> к опциям.
scala> some(10) <*> (some(20) map ((_: Int) * (_: Int)).curried)
res18: Option[Int] = Some(200)
И поскольку все аппликативы можно абстрагировать, не обращая внимания на конкретный конкретный тип, вот тот, который смешивает опцию с приложением-функцией через <*> ..
scala> some(9) <*> some((_: Int) + 3)
res19: Option[Int] = Some(12)
Эквивалент Хаскелла этого …
Просто ( + 3 ) <*> Всего 9
Scalaz в полной мере использует две особенности Scala — более высокие типы и импликации. Весь дизайн Scalaz совершенно не похож на обычный дизайн на основе Scala, с которым вы столкнетесь в другом месте. Иногда вы найдете эти реализации довольно непрозрачными и многословными. Но большая часть многословия происходит от того, как мы кодируем классы типов в Scala, используя имплициты. Рассмотрим следующее определение карты, которая доступна как сутенер, определенный в характеристике MA.
sealed trait MA[M[_], A] extends PimpedType[M[A]] {
import Scalaz._
//..
def map[B](f: A => B)(implicit t: Functor[M]): M[B] = //..
//..
}
map принимает чистую функцию (f: A => B) и может применяться к любому конструктору типов M до тех пор, пока она получает экземпляр Functor [M] в своем неявном контексте. Используя эту черту, мы объединяем конструктор определенного типа с помощью функции map.
Вот несколько примеров использования аппликативов и функторов в Scalaz. Для забавы я перевел несколько примеров из « Learn You a Haskell for Great Good» . Я также упоминаю соответствующую версию Haskell для каждого из них ..
// pure (+3) <*> Just 10 << from lyah
10.pure[Option] <*> some((_: Int) + 3) should equal(Some(13))
// pure (+) <*> Just 3 <*> Just 5 << lyah
// Note how pure lifts the function into Option applicative
// scala> p2c.pure[Option]
// res6: Option[(Int) => (Int) => Int] = Some(<function1>)
// scala> p2c
// res7: (Int) => (Int) => Int = <function1>
val p2c = ((_: Int) * (_: Int)).curried
some(5) <*> (some(3) <*> p2c.pure[Option]) should equal(Some(15))
// none if any one is none
some(9) <*> none should equal(none)
// (++) <$> Just "johntra" <*> Just "volta" << lyah
some("volta") <*> (some("johntra") map (((_: String) ++ (_: String)).curried))
should equal(Some("johntravolta"))
// more succinct
some("johntra") |@| (some("volta") apply (_ ++ _) should equal(Some("johntravolta"))
Скалаз это умопомрачительный. Это заставляет вас думать по-другому. В этом посте я только поцарапал поверхность и рассказал о паре классов классов. Но единственный способ изучить Scalaz — это пройти через кодовую базу. Это плотно, но если вам нравится функциональное программирование, вы получите много ага! моменты, проходящие через это.
В следующем посте я расскажу, как я перевел часть модели предмета системы финансовой торговли, которую я написал в Haskell ( часть 1 , часть 2 и часть 3 ), на Scalaz. Это было забавное упражнение для меня и показывает, как вы можете написать свой код Scala в аппликативном стиле.
С http://debasishg.blogspot.com/2010/11/exploring-scalaz.html