Комментарии 15
Раз уж статья про ФП, то так и хочется убрать сайдэффекты:
Кстати, не понятно зачем писать def grind: A => B, когда достаточно val.
import scalaz._, Scalaz._
object Main extends App {
case class Wheat()
case class Flour()
case class Dough()
case class Bread()
type Result[T] = String \/ T
type Log[T] = WriterT[Result, Vector[String], T]
def write(s: String): Log[Unit] = WriterT.writerT(\/-(Vector(s) -> ()))
def grind(w: Wheat): Log[Flour] = for {
_ <- write("make the flour")
} yield Flour()
def kneadDough(f: Flour): Log[Dough] = for {
_ <- write("make the dough")
} yield Dough()
def distributeDough(d: Dough): Log[Seq[Dough]] = for {
_ <- write("distribute the dough")
} yield Seq[Dough]()
def bake(temperature: Int, duration: Int, sd: Seq[Dough]): Log[Seq[Bread]] = for {
_ <- write(s"bake the bread, duration: $duration, temperature: $temperature")
} yield Seq[Bread]()
val bakeRecipe1 = (bake _).curried(350)(45)
val result = grind(Wheat()) >>= kneadDough >>= distributeDough >>= bakeRecipe1
result.run match {
case -\/(e) => println(s"Error: $e")
case \/-((log, res)) => log foreach println
}
}
// результат тот же:
// make the flour
// make the dough
// distribute the dough
// bake the bread, duration: 45, temperature: 350
Кстати, не понятно зачем писать def grind: A => B, когда достаточно val.
+2
Ну статья то как раз о композиции функций на scala и f#, которые чистыми функциональными языками не являются.
По def vs val, тут scala более permissive чем f#, только и всего.
По def vs val, тут scala более permissive чем f#, только и всего.
0
def — это функция, значение которой будет вычисляться при каждом вызове, а val — значение, которое вычисляется лишь однажды.
0
def — метод. В статье метод возвращает функцию, причем каждый раз одинаковую.
Подробнее: What is the difference between “def” and “val” to define a function.
Подробнее: What is the difference between “def” and “val” to define a function.
+2
Замечу, что «если что-то идёт не так», то в Scala лучше использовать монаду Try. Either — это для случая, когда есть два равноправных варианта нормального развития событий. Хотя в качестве примера сгодится.
0
Не совсем. Try — для случая, когда требуется работать с исключениями. Это не самый удобный подход. Для случая «что-то идет не так», если «не так» ограничено строго определенным набором случаев, то гораздо удобнее использовать Validation, \/ — прямые аналоги Either, для которых известно что является правильным, а что — неправильным вариантом. Аналогичный подход — Result в Rust.
Try не позволяет указать как именно «не так» может это «что-то» пойти.
Try не позволяет указать как именно «не так» может это «что-то» пойти.
+2
Второе подчеркивание лишнее.
К сожалению первое не опустить — автоматическое приведение метода к функции работает только для аргументов.
К сожалению первое не опустить — автоматическое приведение метода к функции работает только для аргументов.
0
Аналог >> из статьи — это не compose, а andThen.
0
При всей моей любви к scala, композиция в ней сделана отвратно. Поэтому, в своей библиотечке я добавил следующий синтаксис для композиции:
implicit class EnrichedFunction[-A,+B](f: A => B) {
/**
* Shortcut for function composition
* @return a composition of functions
*/
def o [R] (g: R => A): R => B = x => f(g(x))
def ∘ [R] (g: R => A): R => B = x => f(g(x))
}
Собственно, делает то же самое что и compose (можете сравнить код), однако, по моему-мнению выглядит более юзабельно.
0
Композиция функций с сайд эффектами весьма интересна так как позволяет понять применимость моноидов и комоноидов. hodzanassredin.github.io/2016/01/24/monoid_meets_comonoid.html
0
Я думал ROP перевели сотню лет назад, но нет.
Здесь все радостно и понятно.
fsharpforfunandprofit.com/rop
Здесь все радостно и понятно.
fsharpforfunandprofit.com/rop
+1
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Композиция функций на F# и Scala