Haskell - let/где эквивалентно в понимании списка?

Есть ли способ использовать let, where или иначе определять подвыражения в понимании списка, чтобы он мог использоваться как в терминах, так и в ограничении?

Из моих экспериментов, следующая работа:

[let x = i*i in x | i<-[1..10], i*i > 20]   --good
[i*i | i<-[1..10], let x=i*i in x > 20]     --good

Но это не bc области:

[let x = i*i in x | i<-[1..10], x > 20]  -- 'x' not in scope error
let x = i*i in [x | i<-[1..10], x > 20]  -- 'i' not in scope error
[x | i<-[1..10], x > 20] where x = i*i   --parse error on 'where'

Итак, let работает в одном или другом месте, но не вместе!

Единственное, что я нашел, чтобы заставить его работать (т.е. избегать повторяющихся выражений и, возможно, оценок), - добавить глупый одиночный список, как я здесь, с x<-[cat i [1..k] в качестве ограничения для понимания списка:

> let cat x l = foldl1 (++) $ map show [x*i | i<-l]
maximum [x| i<-[1..9999], k<-[2..div 10 $ length $ show i], x<-[cat i [1..k]], sort x == "123456789"]
"932718654"

Или, рассмотрев тривиальный пример выше,

[x | i<-[0..10], x<-[i*i], x > 20] --works

Это кажется немного глупым, и немного не хватает ясности, это не кажется слишком неэффективным. Тем не менее, было бы неплохо, если бы let или where работали во всем понимании. Это можно сделать?

Ответ 1

Вы пишете это следующим образом:

[x | i <- [0..10], let x = i*i, x > 20]

Обратите внимание, что нет in. Вы можете ссылаться на x как на термин, так и на любые ограничения, следующие за let. Эта форма let соответствует таковой в do -notation:

do i <- [0..10]
   let x = i*i
   guard (x > 20)
   return x

Здесь x находится в области видимости от let до конца do -блока.

Ответ 2

У тебя это было почти; вы можете просто написать [x | i <- [0..10], let x = i*i, x > 20] (обратите внимание на , вместо in). Он очень похож на do -notation (на самом деле вы можете использовать do -notation вместо этого, а последнее расширение GHC позволяет использовать списки для произвольных монадов). Если вам интересно, вы можете найти синтаксис в отчете Haskell 98:

aexp -> [ exp | qual_1 , ... , qual_n ] (list comprehension, n >= 1)
qual -> pat <- exp                      (generator)
     |  let decls                       (local declaration)
     |  exp                             (guard)

Как вы можете видеть, один из действительных квалификаторов - let decls, который именно вы хотели.