Как стать автором
Обновить

Комментарии 15

Объяснять про монады в Scala без введения понятия typeclass — это несколько необычно.
Это обобщение будет введено в дальнейшем?

А вообще не легкий это труд объяснять ФП. Мне в свое время очень помог learning Scalaz.
Спасибо за совет, следующую статью я планирую начать с typeclass и implicit, что-бы дать основу для понимания Future.
Спасибо за статью.

Нашёл пример:
scala> val fruits = Seq("apple", "banana", "orange")
fruits: Seq[java.lang.String] = List(apple, banana, orange)

scala> fruits.map(_.toUpperCase)
res0: Seq[java.lang.String] = List(APPLE, BANANA, ORANGE)

scala> fruits.flatMap(_.toUpperCase)
res1: Seq[Char] = List(A, P, P, L, E, B, A, N, A, N, A, O, R, A, N, G, E)


то flatMap — это (=<<) из Хаскеля, bind с аргументами в обратном порядке.
(=<<) :: Monad m => (a -> m b) -> m a -> m b
-- Same as >>=, but with the arguments interchanged.


> (map toUpper) =<< ["banana","lemon","orange"]
"BANANALEMONORANGE"

Да, но изначально в scala эти методы так не позиционируются, как не позиционируется таким образом и SelectMany в LINQ.

В scalaz же есть и typeclass Bind и метод >>=:

List("ab", "cd") >>= {_.toUpperCase.toList}
// List(A, B, C, D)


Сводку (несколько устаревшую и не полную) по typeclass в scalaz можно посмотреть в Scalaz cheatsheet.
Кстати, полагаю вам будет интересно, что в scala есть и аналог do-notation — for-comprehensions:

for {
  f <- fruits
  //f2 = f.drop(1)
  c <- f.toUpperCase
  //if c != 'A'
} yield c


Правда есть отличие: последнее выражение в for — это map, а не flatMap.
Что это я? Они же есть в статье.
for-comprehensions — это do-notation или всего лишь list-comprehension?
Требуется только наличие map и flatMap (для использования if требуется withFilter, но if можно просто не использовать). Работает для коллекций, Option, Try и многих других классов. При наличии соответствующей реализации typeclass Monad будет работать для любого класса.
Скорее monad-comprehensions, т.е. то же, что и list-comprehensions, но для произвольной монады.
> Что касается функции unit, то она отвечает за создание монады и для каждой монады она отличается. Для примера, функция unit.

Насколько я понимаю, в Scala вместо unit используется apply. Таким образом:

«для монады Option это Some(x)», не совсем верно, скорее Option.apply(x)
«для монады List это List(x)» – List.apply(x)
«для монады Try это Success(x)» – Все-таки Try.apply(x), т.к. x тут это анонимная ф-я «x: => T»
Все-таки на Option(x) есть дополнительная логика — если x == null, то вернется None.

Но если хотите полностью функционального подхода, то в scalaz есть typeclass `Applicative`:

1.point[Option]
1.point[List]

import scalaz.contrib.std.utilTry._
1.point[Try]
Совершенно необязательно иметь определенную функцию для unit.
Она есть в теории, но на практике она в основном by convention, так же она может иметь разные сигнатуры.

apply — это тоже договоренность, которая просто позволяет применять синтаксис вызова функции.
Например: вызов List(x) — это вызов функции apply на объекте List.
В вашем примере используется map
address.map(add => println("Address : " + add)).getOrElse(println("Error"))
// Address : my.host.com/82.98.86.171:22

хотя, результат этого выражения Unit. Логичнее было бы использовать foreach, или вынести println за скобки.
println(address.map(add => "Address: " + add).getOrElse("Error"))

Да, вы правы, лучше было бы использовать foreach, но так как в статье я упоминал про map, и хотел показать пример именно с ним.
Что касается функции unit, то она отвечает за создание монады и для каждой монады она отличается. Для примера, функция unit.

для монады Option это Some(x)
для монады List это List(x)
для монады Try это Success(x)

Разве Try является монадой? У нее же нарушается left unit law…
Пример:
import scala.util.Try

def f[T]: T => Try[T] = x => throw new Throwable
val num: Int = 5

Try(num) flatMap f

f(num)

Результаты последних двух выражений отличаются, хотя для монады должны быть равны.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории