Вопрос

I was learning some new Haskell today, when I tried something in ghci. It basically boiled down to this:

Prelude> let x = 6
Prelude> x
6
Prelude> let y = show x
Prelude> y
"6"
Prelude> let x = show x
Prelude> x
"\"\\\"\\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\" --(repeats)

So can ghci not self-reference in assignment? I feel like it's akin to i = i++; in C, or trying to reference previous assignments of a let (not let*) in Scheme. Is there anyway to do this, or should I just use the easier let y = show x?

Это было полезно?

Решение

Definitions in Haskell are recursive by default. So the definition you made for x refers to the very same x, which is the reason for the very long string you are seeing, because x is defined to a String that is the result of calling show on itself, so you keep seeing the opening quotes for showing a string, and then those opening quotes escaped, and so on.

These kinds of definitions can be surprisingly useful. For example if you write:

let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

you can define the Fibonacci sequence in terms of itself as an infinite list.

Then you can do something like take 10 fibs to just see the first 10 elements.

Другие советы

It's self referencing indeed! It's trying to solve the equation

x = show x

Since show returns a string Haskell knows that x begins like "\"" and that's enough to guess the second character, which is enough for the third which is enough for the fourth...

And so on.

Yes, you can do a not-selfreferencing assignment in ghci, although it's a bit cumbersome:

let x = 5
x <- return $ show x

lets are recursive, while monadic bindings are not.

Again: yes, Haskell very much can self-reference in assignment, otherwise this would just give an error instead of printing something undecipherable, wouldn't it?

What you can not do in Haskell, ever, is modify / re-assign the value of a variable. It's just totally out of question: if you've once defined x as some value, x will keep this value forever. An assumption baked into the very language, and used to great avail by the compiler for lots of optimisations etc..

Now you wonder why you can write let x = ... again at all, didn't I just say this is not possible? The thing is, what you're doing there is define a new variable that also happens to have the name x, but that doesn't change anything about the old variable with the same name. You might expect this

Prelude> let x = 6
Prelude> let p = print x
Prelude> let x = 7
Prelude> p

to yield 7, but actually it prints 6 because p is still referring to the old variable x, not to the new one that was defined only later.

Still confused? Perhaps less strange is something like

n :: Int
n = 7

f :: IO ()
f = print $ replicate n "ha"

... -- much later, you've forgotten there was a global `n` up there...

g :: String -> String
g = take n
 where n = 37

It's quite reasonable that f will take 37 characters, not 7, while any call to f continues to repeat the string only 7 times. After all, it's "g, where n has that value", but nothing else outside. Now, let is just where written the other way around. In particular, a do notation or your GHCi prompt is actually syntactic sugar for something like this:

  let x = 6
  in ( print x >> ( let y = show x
                    in ( print y >> ( let x = show x
                                      in ( print x )
                                    )
                       )
     )

Each paren encloses a scope. Variables defined in outer scopes can be used, but local ones are preferred if found. So let x = show x in ( print x ) can be considered completely on its own, the original x = 6 is shadowed out of scope here. Therefore, the only way the definition can work is refer recursively to itself.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top