<-
and let
simply mean different things.
When you write y <- [x,x]
, you are saying "give y
each of the values inside the list [x,x]
in turn".
When you write let y = [x,x]
, you are saying "give y
the value [x,x]
".
문제
I'm new to haskell and I am trying to learn haskell. I am trying to create simple functions like "duplicate" in the most possible ways i can think. I have already done "duplicate" with pattern matching and guards. I can't get it working with a let inside a list comprehension. I can imagine let inside a list comprehension is not ideally but I am more interested why it does not work =].
My question is: why does duplicate' produces [Int] and duplicate'' produces [[Int]] and is it even possible to produce [Int] with a let inside a list comprehension?
Thank you for your time and help :).
--list comprehension
duplicate' xs = [y | x <- xs, y <- [x,x]]
input => [1,2,3,4]
output => [1,1,2,2,3,3,4,4]
expected? => yes
--list comprehension with let
duplicate'' xs = [y | x <- xs, let y = [x,x]]
input => [1,2,3,4]
output => [[1,1],[2,2],[3,3],[4,4]]
expected? => yes
해결책
<-
and let
simply mean different things.
When you write y <- [x,x]
, you are saying "give y
each of the values inside the list [x,x]
in turn".
When you write let y = [x,x]
, you are saying "give y
the value [x,x]
".
다른 팁
let
does nothing but define a new symbol to take one some given value. You could always just inline the definition by hand:
[ y | x <- xs, let y = [x,x] ] ≡ [ [x,x] | x <- xs ]
Any such expression with only one <-
has thus the form
[ f x | x<-xs ]
which is equivalent to map f xs
. So the result list must then always have the same length as xs
, making the desired behaviour of duplicate
impossible to achieve: if you want to have duplicates, you need to encapsulate them as inner lists so they won't count as more elements.
To merge these nested lists back into a "flat" one, you can utilise the fact that lists are a monad:
join :: Monad m => m (m a) -> m a
Prelude Control.Monad> join [[1,1], [2,2]]
[1,1,2,2]
Now, join
is in fact how category theorists prefer to define monads, but as you may know Haskell does it a bit differently:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
a >>= f = join (fmap f a)
or, as it is actually defined the other way around,
join a = a >>= id
Put that into the modified let
version of 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
Now, an expression do { a<-p; b<-q; ... return x }
is a monad comprehension, the generalisation of list comprehensions. It can be rewritten [x | a<-q, b<-q, ...]
. For our problem,
join [y | x <- xs, let y = [x,x] ] ≡ [y | x<-xs, y<-[x,x]]
which is where you started. It is inevitable to use two <-
s when doing this with a pure list comprehension.
Of course you could still use also a let, at any point...
[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]
≡ ...