Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the role of 'bottom' (⊥) in Haskell function definitions?

Tags:

I don't understand the role played by bottom ( or _|_) in Haskell function definitions.

The definition of zip for example describes it as "right lazy" because

zip [] _|_ = [] 

but I'm unclear how this differs from

zip [] _ = [] 

What role is _|_ playing in function definitions such as the one above? In particular, how is it different from using _?


UPDATE AND NOTE: As readers of the excellent answers will discover for themselves, a crucial part of those answers, worth pulling up here, is that does not (and cannot), in fact, appear in Haskell function definitions. Read on.

like image 906
orome Avatar asked Sep 10 '15 15:09

orome


People also ask

What is bottom in Haskell?

From HaskellWiki. The term bottom refers to a computation which never completes successfully. That includes a computation that fails due to some kind of error, and a computation that just goes into an infinite loop (without returning any data). The mathematical symbol for bottom is '⊥'.

What is the function of bottom?

The bottom() function ranks the time series described by the expression, and then uses that ranking to represent each time series as either the constant 1 or the constant 0: 1 is returned for each time series that is in the specified number of series at the bottom of the ranking.

How do you define a function in Haskell?

The most basic way of defining a function in Haskell is to ``declare'' what it does. For example, we can write: double :: Int -> Int double n = 2*n. Here, the first line specifies the type of the function and the second line tells us how the output of double depends on its input.


2 Answers

Bottom is essentially the fancy algebraic way of saying undefined.

If you try this, you can see why zip is lazy for its right-hand argument:

λ> zip [] undefined [] λ> zip undefined [] *** Exception: Prelude.undefined 

This is because undefined only fails when you try to evaluate it.

You might be confusing _|_ with _ because of the way it was presented. I will make it clear: the line zip [] _|_ = [] does not act as a pattern match but an equation, stating the equality of zip [] _|_ and []. That is to say, this is not valid Haskell code, but a notational, abstract-algebraic way of saying "I don't care about the second argument."

In the definition of zip you may of course use _, but that's irrelevant. You could have used any name, just as long as it wasn't a constructor-matching pattern such as (Just x) or (a,b). Values will remain unevaluated until they must be pattern matched in pure code.

You can read more about lazy evaluation here.

You can read more about bottom here and here.

like image 69
AJF Avatar answered Oct 22 '22 18:10

AJF


I think the OP already realises this, but for the benefit of others who come here with the same confusion: zip [] _|_ = [] is not actual code!

The symbol _|_ (which is just an ascii-art rendering of the mathematical symbol ) means bottom1, but only when we're talking about Haskell. In Haskell code it does not have this meaning2.

The line zip [] _|_ = [] is a description of a property of the actual code for zip; that if you call it with first argument [] and pass any bottom value as the second argument, the result is equal to []. The reason they would want to say exactly this is because the technical definition of what it means for a function f to be non-strict is when f ⊥ is not .

But there is no role of _|_ (or , or undefined, or the concept of bottom at all) in defining Haskell functions (in code). It has to be impossible to pattern match on an argument to see whether it is , for a number of reasons, and so there is no actual symbol for in Haskell code3. zip [] _|_ = [] is documentation of a property that is a consequence of the definition of zip, not part of its definition.

As a description of this property zip [] _ = [] is a less specific claim; it would be saying that whatever you call zip [] on, it returns []. It amounts to exactly the same thing, since the only way zip [] ⊥ can return something non-bottom is if it never examines its second argument at all. But it's speaking less immediately to the definition of non-strict-ness.

As code forming part of the definition of the function zip [] _ = [] can't be compared and contrasted to zip [] _|_ = []. They're not alternatives, the first is valid code, and the second is not.


1 Which is the "value" of an expression that runs forever, throws an exception, or otherwise falls to evaluate to a normal value.

2 It's not even a valid Haskell identifier, since it contains both "namey" characters (_) and "operator" characters (|). So it can't actually be a symbol meaning anything at all in Haskell code!

3undefined is often used for , but it's more of a variable referring to a value than the actual thing itself. Much like if you have let xs = [1, 2, 3] you can use xs to refer to the list [1, 2, 3], but you can't use it as a pattern to match some other list against; the attempted pattern match would just be treated as introducing a new variable named undefined or xs shadowing the old one.

like image 37
Ben Avatar answered Oct 22 '22 19:10

Ben