опять лень
Oct. 26th, 2014 12:07 pmкак это уже известно, в хаскеле всё по умолчанию ужасно ленивое до такой степени что иногда, в редких к счастью случаях, приходится даже строго форсить вычисление (это в хаскеле так, понятно вобщем наверное :)
в скале же ситуация полностью наоборот противоположная - по умолчанию все вычисления строгие и привычные среднему интеллекту, но при остром желании конечно можно сделать вычисление достаточно ленивым с помощью некоторых нехитрых в принципе механизмов (это так в скале конечно же).
это вобще к чему я повторяю такие известные всем трюизмы? сравнительно недавно прочитал в списке рассылки такие вот примерно слова нашего уважаемого runarorama:
The problem with this kind of thing is almost always that something is too strict. In this case there are a few places where we are not quite lazy enough. One of those is the (,) Tuple2 constructor in unfold.
When we say (x, y) in Scala, both x and y are evaluated strictly, because this constructor is strict in both of its arguments.
A solution here is to use a lazy pair data type:
case class LazyPair[A,B](a: () => A, b: () => B)
ну и так далее (там дальше Рунар развивал эту тему, и правильно вобщемто делал). Мне с одной стороны это напомнило мои недавние развлечения с прямо противоположной задачей в хаскеле - нужно было сделать строгой операцию пары (,) которая в хаскеле как раз ленивая.
но в данном случае я хотел сказать немного другое (это всё было как бе предисловие). я вот о чем на досуге подумал както - а почему собственно нужно определять LazyPair? то есть вопрос собственно в том что если делать всё таким образом то получится нужно для каждого отдельного случая (структуры данных) определять отдельную ленивую версию. и действительно - я посмотрел scalaz и вижу что там много таких ленивых структур данных:
с одной стороны конечно в этом возможно и есть какойто глубокий смысл, но с другой стороны а что если мне нужно еще что нибудь сделать ленивым? мне что полностью переопределять весь тип заново чтобы создать его ленивый вариант? а что если вместо этого просто определить следующий тип:
и тогда например можно так написать (ну это только пример для аналогии с тем что Рунар показывал, думаю понятно конечно):
будет ли это чем нибудь плохо? ну есть конечно определенный недостаток - меняется тип: вместо просто T он становится уже Lazy[T]. но даже если считать это и недостатком (что еще не факт но допустим) то есть зато другое серьезное преимущество - мы можем делать ленивой вообще любую существующую уже в скале структуру данных. то есть мы можем произвольным образом в любой момент перейти от строгих вычислений к ленивым - и даже в типе данных этот факт будет отражен, что на самом деле может быть даже очень и полезно (факт отражения ленивости в типе).
написать что ли им об этом в это их спортлото?
это ж монада получается даже:
в скале же ситуация полностью наоборот противоположная - по умолчанию все вычисления строгие и привычные среднему интеллекту, но при остром желании конечно можно сделать вычисление достаточно ленивым с помощью некоторых нехитрых в принципе механизмов (это так в скале конечно же).
это вобще к чему я повторяю такие известные всем трюизмы? сравнительно недавно прочитал в списке рассылки такие вот примерно слова нашего уважаемого runarorama:
The problem with this kind of thing is almost always that something is too strict. In this case there are a few places where we are not quite lazy enough. One of those is the (,) Tuple2 constructor in unfold.
When we say (x, y) in Scala, both x and y are evaluated strictly, because this constructor is strict in both of its arguments.
A solution here is to use a lazy pair data type:
case class LazyPair[A,B](a: () => A, b: () => B)
ну и так далее (там дальше Рунар развивал эту тему, и правильно вобщемто делал). Мне с одной стороны это напомнило мои недавние развлечения с прямо противоположной задачей в хаскеле - нужно было сделать строгой операцию пары (,) которая в хаскеле как раз ленивая.
но в данном случае я хотел сказать немного другое (это всё было как бе предисловие). я вот о чем на досуге подумал както - а почему собственно нужно определять LazyPair? то есть вопрос собственно в том что если делать всё таким образом то получится нужно для каждого отдельного случая (структуры данных) определять отдельную ленивую версию. и действительно - я посмотрел scalaz и вижу что там много таких ленивых структур данных:
object LazyEither extends LazyEitherInstances with LazyEitherFunctions object LazyEitherT extends LazyEitherTInstances with LazyEitherTFunctions with Serializable object LazyOption extends LazyOptionInstances with LazyOptionFunctions with Serializable object LazyOptionT extends LazyOptionTInstances with LazyOptionTFunctions with Serializable object LazyTuple extends LazyTupleFunctions object LazyTuple2 extends LazyTuple2Instances object LazyTuple3 extends LazyTuple3Instances object LazyTuple4 extends LazyTuple4Instances
с одной стороны конечно в этом возможно и есть какойто глубокий смысл, но с другой стороны а что если мне нужно еще что нибудь сделать ленивым? мне что полностью переопределять весь тип заново чтобы создать его ленивый вариант? а что если вместо этого просто определить следующий тип:
class Lazy[T](f: => T) { lazy val get = f def apply() = get } object Lazy { def apply[T](f: => T) = new Lazy(f) }
и тогда например можно так написать (ну это только пример для аналогии с тем что Рунар показывал, думаю понятно конечно):
type LazyPair[A,B] = (Lazy[A],Lazy[B])
будет ли это чем нибудь плохо? ну есть конечно определенный недостаток - меняется тип: вместо просто T он становится уже Lazy[T]. но даже если считать это и недостатком (что еще не факт но допустим) то есть зато другое серьезное преимущество - мы можем делать ленивой вообще любую существующую уже в скале структуру данных. то есть мы можем произвольным образом в любой момент перейти от строгих вычислений к ленивым - и даже в типе данных этот факт будет отражен, что на самом деле может быть даже очень и полезно (факт отражения ленивости в типе).
написать что ли им об этом в это их спортлото?
это ж монада получается даже:
implicit val m = new Monad[Lazy] { def point[A](a: => A) = Lazy(a) def bind[A, B](fa: Lazy[A])(f: A => Lazy[B]) = Lazy(f(fa())()) }