blue-sky
blue-sky

Reputation: 53896

Why does this cause GHCI to hang?

This code :

y :: Int
y = y + 1

When executed causes GHCI to hang.

y :: Int; this means y is of type Int
y = y + 1; this means y is defined to be an Int + 1

Please correct me if I'm incorrect in definitions of statements.

Why does y not evaluate ?

Is reason that y is being added to an Int, but its just being added to a type not to a value ?

Upvotes: 1

Views: 358

Answers (3)

AJF
AJF

Reputation: 11923

Haskell has no variables, only constants, thus, you cannot use the same style as in other languages, with updatable values referring to the last. It does mean, however, that you can do some pretty awesome things, which you've tripped upon.

Take this declaration as an example:

myList = 1 : myList

When evaluated, this will refer to itself, thus doing this:

myList = 1 : myList                      -- but I'm referring to myList, so...
myList = 1 : (1 : myList)                -- but I'm referring to myList, so...
myList = 1 : (1 : (1 : myList))          -- but I'm referring to myList, so...
myList = 1 : 1 : 1 : 1 : 1 : 1...        -- etc.

The same goes for your constant y:

y = y + 1             -- but I'm referring to y, so...
y = y + 1 + 1         -- but I'm referring to y, so...
y = y + 1 + 1 + 1     -- but I'm referring to y, so...
y = 1 + 1 + 1 + 1 ... -- etc.

GHCi can never completely evaluate the value of y, because it's infinite, causing GHCi to hang.

Upvotes: 3

CR Drost
CR Drost

Reputation: 9817

Speaking more broadly, a Haskell file (or GHCi) does NOT contain a list of imperatives/orders, like some other programming languages. It is a different style of programming language. Instead there are a handful of kinds of top-level statements which you have access to:

  1. You can define values. y = y + 1 defines the symbol y to be an application of a function (+) to two other parameters, y and 1. This definition holds throughout the file, in particular to things above the definition and within the definition. So, you can totally write y = x + 1 and then x = 2 in a .hs file and ask GHCi for y and it will say 3. Note that this gets more complicated with the let keyword, which forms a "wall" to this expansive nature of definition: let accepts a block of definitions and a value-expression and scopes those definitions to within the combined (block-of-definitions, value-expression) context, but walls off those definitions from the world outside the let. So this is valid, too:

    Prelude> let y = x + 1; x = 2
    Prelude> y
    3
    
  2. You can define data structures and their constructors. A constructor is a special function which we allow to participate in pattern-matching: in other words, Haskell knows how to invert or destructure every constructor. You can also define a type synonym and a newtype, which is halfway in-between those.

  3. You can provide metadata about individual values (type declarations). These are really helpful for narrowing down where a type error is because they set up a "wall" for the type inference algorithm. They also can have semantic effects either in adding polymorphism (Haskell has a "monomorphism restriction" which often bites newcomers) or in restricting that polymorphism to a concrete type.

  4. You can provide metadata about the package as a whole: both how it incorporates other packages (import statements) and how it can be used by other packages (module statements).

None of these are orders that you're giving to the Haskell system; instead your file is all one big description of a module. Similarly within an expression (the first part above) there are only a couple of things you can do, and they are not usually imperatives: you can apply values to other values, create functions, you can create local definitions (let) and pattern-match (case), and you can locally add type metadata. Everything else, including do notation, is just a more convenient way ("syntactic sugar") to do those things above.

Your two statements are a type declaration ("the type of the y defined by this module will be an integer") and a definition ("to compute a y as defined by this module, first compute the value of a y, then add one to it"). Haskell reads both of them together and says, "oh, y has type Int, so (+) is the specific Int-Plus operation that I know, (+) :: Int -> Int -> Int, and then 1 is the specific Int of that name which I know... . It will then confirm that the types are self-consistent and produce some imperative code which loops forever.

Upvotes: 3

Sibi
Sibi

Reputation: 48766

That's because it recurses infinitely. You are calling y which is defined as y + 1. So how will the evaluation proceed ?

It goes like this:

y
y + 1
(y + 1) + 1
((y + 1) + 1) + 1

and so on...

Upvotes: 4

Related Questions