Just started Haskell, it's said that everything in Haskell is "immutable" except IO package. So when I bind a name to something, it's always something immutable? Question, like below:
Prelude> let removeLower x=[c|c<-x, c `elem` ['A'..'Z']]
Prelude> removeLower "aseruiiUIUIdkf"
"UIUI"
So here:
1. “removeLower" is an immutable? Even it's a function object?
But I can still use "let" to assign something else to this name.
2. inside the function "c<-x" seems that "c" is a variable.
It is assigned by list x's values.
I'm using the word "variable" from C language, not sure how Haskell name all its names?
Thanks.
If you're familiar with C, think of the distinction between declaring a variable and assigning a value to it. For example, you can declare a variable on its own and later assign to it:
Or you can declare a variable and assign initial value at the same time:
And in either case, you can mutate the value of a variable by assigning to it once more after the first initialization or assignment:
Assignment in Haskell works exclusively like the second example—declaration with initialization:
I bolded and hyperlinked "scope" because it's the second critical component here. This goes one of your questions:
After you bind
removeLower
to the function you define in your example, the nameremoveLower
will always refer to that function within the scope of that definition. This is easy to demonstrate in the interpreter. First, let's define a functionfoo
:Now we define an
bar
that usesfoo
:And now we "redefine"
foo
to something different:Now what do you think happens to
bar
?It remains the same! Because the "redefinition" of
foo
doesn't mutate anything—it just says that, in the new scope created by the "redefinition", the namefoo
stands for the function that adds three. The definition ofbar
was made in the earlier scope wherefoo x = x + 2
, so that's the meaning that the namefoo
has in that definition ofbar
. The original value offoo
was not destroyed or mutated by the "redefinition."In a Haskell program as much as in a C program, the same name can still refer to different values in different scopes of the program. This is what makes "variables" variable. The difference is that in Haskell you can never mutate the value of a variable within one scope. You can shadow a definition, however—uses of a variable will refer to the "nearest" definition of that name in some sense. (In the case of the interpreter, the most recent
let
declaration for that variable.)Now, with that out of the way, here are the syntaxes that exist in Haskell for variable binding ("assignment"). First, there's top-level declarations in a module:
Here the name
addTwo
is declared with the given function as its value. A top level declaration can have private, auxiliary declarations in awhere
block:Then there's the
let ... in ...
expression, that allows you to declare a local variable for any expression:Then there's the
do
-notation that has its own syntax for declaring variables:The
var <- action
assigns a value that is produced by an action (e.g., reading a line from standard input), whilelet var = expr
assigns a value that is produced by a function (e.g., concatenating some strings). Note that thelet
in ado
block is not the same thing as thelet ... in ...
from above!And finally, in list comprehension you get the same assignment syntax as in
do
-notation.It's referring to the monadic bind operator
>>=
. You just don't need to explicitly write a lambda as right hand side parameter. The list comprension will be compiled down to the monadic actions defined. And by that it means exactly the same as in a monadic environment.In fact you can replace the list comprension with a simple call to
filter
:To answer your question regarding the
<-
syntactic structure a bit clearer:is the same as
is the same as
is the same as
Consider the official documentation of Haskell for further reading: https://hackage.haskell.org/package/base-4.8.2.0/docs/Control-Monad.html#v:-62--62--61-
is a list comprehension, and
c <- x
is a generator wherec
is a pattern to be matched from the elements of the listx
.c
is a pattern which is successively bound to the elements of the input listx
which area
,s
,e
,u
, ... when you evaluateremoveLower "aseruiiUIUIdkf"
.is a predicate which is applied to each successive binding of
c
inside the comprehension and an element of the input only appears in the output list if it passes this predicate.