Is there any way to use a where expression inside the list syntax? I imagined something like this would work:
foo = [i where i = 1]
But compilation yields:
error: parse error on input ‘where’
It's possible to use let ... in inside a list:
foo = [let i = 1 in i]
So I assume that where and let behave as different syntactic constructs?
I'm aware it's possible to do:
foo = [i] where i = 1
... but for more complicated lists the distance between the i and the i = 1 is too far for it to be clear - see the context section below and imagine many more test cases, and this testGroup itself nested inside a list.
Context
I ran across this while trying to write a readable testcase:
testGroup "toInput"
[
testProperty "toInput '*'" (forAll others conversionFails)
where others = arbitrary `suchThat` (\c -> c `notElem` exclChars)
conversionFails c = toInput c == Nothing
]
The equivalent with let just isn't as satisfying to read imo (and, more concretely, when surrounded by other test cases, the testProperty not being on the same indentation as them makes it harder to follow).
testGroup "toInput"
[
let others = arbitrary `suchThat` (\c -> c `notElem` exclChars)
conversionFails c = toInput c == Nothing
in
testProperty "toInput '*'" (forAll others conversionFails)
]
Like the Haskell Wiki article says:
It is important to know that
let ... in ...is an expression, that is, it can be written wherever expressions are allowed. In contrast,whereis bound to a surrounding syntactic construct, like the pattern matching line of a function definition.
So in short, where is bound to the clause of a function declaration, whereas let is more locally scoped in an expression. If you write where, it is visible for the entire clause, like:
f a | a > 0 = g b b
| otherwise = b
where b = g a a
so here the where is visible for the two guards, a let is more locally scoped. If you write let y = f x in y * y, then that y is only visible in the in ... part. For example we can write:
Prelude> let a = 4 in (let a = 2 in a) + a
6
So here the inner a is 2 whereas the outer is 4. This might end up to be very confusing. If we would define variables with the same name in a where clause, this would result in name conflicts.
In case your code sample is a single function, you can of course move this out of the list definition, like:
foo exclChars =
testGroup "toInput" [testProperty "toInput '*'" (forAll others conversionFails)]
where others = arbitrary `suchThat` (`notElem` exclChars)
conversionFails = isNothing . toInput
[1 | 1==2] is a list comprehension too. 1==2 is a Boolean expression.
In your example foo = [let i = 1 in i], the let i = 1 in i is an expression as well.
Indeed where and let are different. let forms an expression, but where does not - it is a part of a definition (a = b where ...).
Difference is, a variable defined by where can be used in the definition's guards, whereas let goes on the right hand side of the = sign.
To your question, just move ] before the where, and it will become part of the definition where it appears.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With