Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a list comprehension accept mixed `[Char]` and `[[Char]]` without error in Haskell?

While learning Haskell I found something which puzzled me.

I don't understand why this code is valid:

Prelude> [y | y <- "a", y <- ["a"]]
["a"]

I tried to change to an explicit [Char] and got the same result (which makes sense):

Prelude> [y | y <- ['a'], y <- ["a"]]
["a"]

Surprisingly this is valid too :

Prelude> [y | y <- "a", y <- [["a"]]]
[["a"]]

[EDIT] the error given is not related to the same fact :

This on the contrary is invalid as I would expect:

Prelude> [y | y <- 'a', y <- ['a']]
<interactive>:12:11: error:
* Couldn't match expected type `[t0]' with actual type `Char'
* In the expression: 'a'
  In a stmt of a list comprehension: y <- 'a'
  In the expression: [y | y <- 'a', y <- ['a']]

[y | y <- 'a'] is invalid because 'a' is not a list.

[/EDIT]

I thought it was some usual mess caused by String vs Char but definitely not:

Prelude> [y | y <- [1], y <- [[2]]]
[[2]]

For the record I'm using GHCi version 8.2.2 and Arch Linux.

like image 813
Pierrot Avatar asked Feb 05 '23 01:02

Pierrot


1 Answers

Turn on warnings! You should see that the first y is being shadowed by the second one.

It is somehow similar to

let y = True
in let y = "a"
in y

where the first definition is overshadowed, as if it were

let _ = True
in let y = "a"
in y

or even, removing the shadowed binding,

let y = "a"
in y

Similarly, a list comprehension such as

[y | y <- "a", y <- ["a"]]

evaluates to the same result as

[y | _ <- "a", y <- ["a"]]
-- or, if you prefer
[y | x <- "a", y <- ["a"]]

Note that, unlike the let above, in list comprehensions we can not simply remove the overshadowed binding y <- .... For instance,

[y | y <- [1,2], y <- [3,4]]

produces [3,4,3,4], unlike [y | y <- [3,4]].

Here you can see the produced warning for each examples :

Prelude> :set -Wall

Prelude> [y | y <- "a", y <- ["a"]]

<interactive>:41:6: warning: [-Wunused-matches]
    Defined but not used: `y'

<interactive>:41:16: warning: [-Wname-shadowing]
    This binding for `y' shadows the existing binding
      bound at <interactive>:41:6
["a"]

Prelude> [y | x <- "a", y <- ["a"]]

<interactive>:47:6: warning: [-Wunused-matches]
    Defined but not used: `x'
["a"]

-- no warning here
Prelude> [y | _ <- "a", y <- ["a"]]
["a"]

Prelude> [y | y <- [1,2] , y <- [3,4]]

<interactive>:49:1: warning: [-Wtype-defaults]
    * Defaulting the following constraints to type `Integer'
        (Show a0) arising from a use of `print' at <interactive>:49:1-29
        (Num a0) arising from a use of `it' at <interactive>:49:1-29
    * In a stmt of an interactive GHCi command: print it

<interactive>:49:6: warning: [-Wunused-matches]
    Defined but not used: `y'

<interactive>:49:12: warning: [-Wtype-defaults]
    * Defaulting the following constraint to type `Integer'
        Num t0 arising from the literal `1'
    * In the expression: 1
      In the expression: [1, 2]
      In a stmt of a list comprehension: y <- [1, 2]

<interactive>:49:12: warning: [-Wtype-defaults]
    * Defaulting the following constraint to type `Integer'
        Num t0 arising from the literal `1'
    * In the expression: 1
      In the expression: [1, 2]
      In a stmt of a list comprehension: y <- [1, 2]

<interactive>:49:19: warning: [-Wname-shadowing]
    This binding for `y' shadows the existing binding
      bound at <interactive>:49:6
[3,4,3,4]
like image 130
chi Avatar answered Feb 16 '23 03:02

chi