let
ничего не значит, но определяет новый символ, чтобы взять какое-то определенное значение. Вы всегда можете просто встраивать определение вручную:
[ y | x <- xs, let y = [x,x] ] ≡ [ [x,x] | x <- xs ]
Любое такое выражение лишь один <-
имеет, таким образом, форму
[ f x | x<-xs ]
что эквивалентно map f xs
. Таким образом, список результатов должен всегда иметь ту же длину, что и xs
, что делает невозможным достижение желаемого поведения duplicate
: если вы хотите иметь дубликаты, вам необходимо инкапсулировать их как внутренние списки, чтобы они не учитывали больше элементов.
Чтобы объединить эти вложенные списки обратно в "плоский" один, вы можете использовать тот факт, что списки являются монада:
join :: Monad m => m (m a) -> m a
Prelude Control.Monad> присоединиться к [[1,1], [2,2]]
[1,1,2,2]
Теперь join
в действительности, как теоретики категории предпочитают определять монады, но, как вы знаете, Haskell делает это немного по-другому:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
a >>= f = join (fmap f a)
или, как это на самом деле определяется наоборот,
join a = a >>= id
Положи в модифицированном let
версии duplicate
:
join [y | x <- xs, let y = [x,x] ]
≡ [y | x <- xs, let y = [x,x] ] >>= id
≡ map (\x -> [x,x]) xs >>= id
≡ xs >>= id . (\x -> [x,x])
≡ xs >>= (\x -> [x,x])
≡ do { x<-xs; [x,x] }
≡ do { x<-xs; y<-[x,x]; return y } -- by the monad laws
Теперь выражение do { a<-p; b<-q; ... return x }
является monad comprehension, обобщение перечня понятий.Его можно переписать [x | a<-q, b<-q, ...]
. По нашей проблеме
join [y | x <- xs, let y = [x,x] ] ≡ [y | x<-xs, y<-[x,x]]
, с которого вы начали. При этом необходимо использовать два <-
с чистым пониманием списка.
Конечно, вы могли использовать также подведенный, в любой точке ...
[y | x<-xs, y<-[x,x]]
≡ [y | x<-xs, let z=[x,x], y<-z]
≡ [a | x<-xs, let z=[x,x], y<-z, let a=y]
≡ [a | x<-xs, let z=let w=[let q=x in q, let r=x in r] in w, y<-z, let a=y]
≡ ...
'[у | x <- xs, y = [x, x]] '==' [[x, x] | x <- xs] 'вы видите, это не то, что вы хотели –