“Everything is a function” in Haskell?

There a belief about Haskell that keeps popping up in chat rooms and mailing lists — one that I’ve been puzzling over for a while. One expression of the belief is “everything is a function” in Haskell.

Of course, there are all of these non-functions that need to be accounted for, including integers, booleans, tuples, and lists. What about them? A recurring answer is that such things are “functions of no arguments” or functions of a one-element type or “constant functions”.

I wonder about how beliefs form, spread, and solidify, and so I asked around about how people came to this notion and how they managed to hold onto it. I had a few conjectures in mind, which I kept to myself to avoid biasing people’s responses. Of the responses I got, some were as I’d imagined, and some were quite surprising to me, revealing some of my blind spots about others’ thinking and about conversation dynamics.

My thanks to the many Haskellers, especially newbies, who took the time to help me understand their thought processes. If you’re interested and in a patient mood, you can see the unedited responses on a Haskell reddit thread and on a #haskell IRC log. There were also a few responses on Twitter.

Edits:

  • 2009-08-04: Added “simplify”: “Would making everything a function really simplify the formal system that is Haskell programming?”. Thanks, SLi.
  • 2009-08-04: Focus on “constant function” story for “It makes things simpler”. I realized that I hadn’t said what I intended there. Thanks, Jonathan Cast.
  • 2011-03-04: Remarks on mutability & dynamic typing, under “Operational thinking”

Analogy with object-oriented programming

Hmm… I think it was because I was originally taught ‘everything is an object’ in OOP.

Many people come to functional programming (FP) after object-oriented programming (OOP). One may have heard that in a “pure OOP” language, everything is an object. By analogy, one could then wonder whether in a “pure FP” language, everything is a function, i.e., whether function is to functional as object is to object-oriented.

Perhaps such an analogy sounds plausible at first, and some people don’t give it critical thought to test whether this first guess fits with reality. Or perhaps they’ve heard someone speak with confidence, and they confused confidence with understanding.

To try to answer the question about the development of beliefs: I think part of it has to do with newcomers hearing the old-hands saying it, and so it becomes part of the dogma they learn.

It makes things simpler

It’s a unification (like electro-weak interaction). Having only one kind of things is better than having two.

Many of us like simple, unifying principles, and we dislike exceptions. Fewer building blocks and fewer exceptions often leads to a simpler, more compelling precise formulation, which supports more tractable reasoning (mechanized in compilers and non-mechanized in programmers).

This reason is perhaps in the realm of wishful thinking. As in “it would be nice if everything were a function in Haskell, since things would be simpler that way”.

Would making everything a function really simplify the formal system that is Haskell programming? For instance, what would be the impact on the type rules?

First, let’s separate the nullary and constant-function perspectives. The (formal) type system has no notion of functions other than unary, so I’ll focus on the constant-function story. For instance, that “7” is shorthand for “() -> 7“, and so “x = 7” means “x = () -> 7“, i.e., “x () = 7” (adopting the usual syntactic sugar). (Edit: added this paragraph.)

If we did decide that values of non-function type are constant functions, then what order functions? 1st, 2nd, 3rd, …? In other words, if 7 is a nullary or constant function, what is the result type of that function? If the answer is, say, Integer, then we’d have to recall that Integer really means nullary or constant function whose result type is Integer, ad infinitum. In a first-order functional language, this troubling question would not arise, and for imperative programmers (including OOP-ers), functions are a mainly first-order concept.

Why complicate your universe with non-functions when functions naturally generalize to cover them?

Similarly, why complicate your universe with functions when non-functions naturally generalize to cover them? For instance, every value (including functions) can be promoted to a list, tree, Maybe, pair, Id (identity newtype wrapper), etc.

But then I wonder what type of thing is in those lists, trees, maybes, etc, and what type of thing is returned from those functions.

And I do like static typing, which is lost when one wraps up everything into a universal type. For instance, we lose knowing that the list or tree has exactly one element, that the Maybe is a Just, and that the function is a constant function.

Mixing up functions and definitions

Many times, I’ve seen Haskell programmers (perhaps mainly newbies) use the word “function” to mean “top-level definition”. Which surprises me, as the meaning I attach to “function” has nothing to do with “top-level” or with “definition”.

For instance,

‘y = 3′ can be considered as a function with no arguments that returns just 3.

I guess what’s going on here is a conflation of functions and (top-level) definitions. Maybe because these two notions are tightly connected in C programming, where functions are always named (defined) and always at the top level and are always immutable, whereas non-functions can be defined in a nested scope and historically were mutable (before const).

Church’s lambda calculus

Well, blame Church.

In Church’s original lambda calculus, everything is a function. For instance,

type Bool = ∀ a. a -> a -> a

false, true :: Bool
true  t e = t
false t e = e

type Nat  = ∀ a. (a -> a) -> (a -> a)

three :: Nat
three f = f ∘ f ∘ f

Do some folks believe we’re still doing what Church did, i.e., to encode all data as functions and build all types out of ->?

Operational thinking

Haskell’s laziness by default was what made me think of constants as functions that take no parameters.

A thunk at the operational level (language implementation) is vaguely reminiscent of a function at the level of language specification and semantics.

This bit of operational reasoning had not occurred to me as a source of thinking that even non-functions are “functions”. I was looking for denotational reasons, i.e., what things mean rather than how an implementation might work. One of my recurring blind spots when trying to understand other’s thought processes is this sort of operational-by-default thinking.

(Edit: added the following paragraph.)

Lazy evaluation replaces thunks by more evaluated forms (pure values for simple types but otherwise an evaluated outer-wrapper and possibly unevaluated inner contents, i.e., weak head normal form). That replacement is done destructively, i.e., a side-effect of evaluation. So the thunk-focused operational-thinking explanation of “everything is a function in Haskell” leads to the interesting conclusion that Haskell has pervasive (and difficult to predict) side-effects and that these side-effects cause changes to types, not just to values (going from function to non-function). That’s a lot of mutability and dynamic typing for a purely functional, statically typed language!

Interested in a different conversation

This one was the hardest of all for me to get so far and was a real lightbulb moment for some people’s belief that everything is a function in Haskell. They don’t believe it. They’d rather talk about something else, such as formalisms in which everything is a function. Or maybe they even have a different notion of what topic is being discussed.

And I realize I often do the same thing. I hear a discussion topic that overlaps with one interesting to me (such as how do people form, spread, and solidify beliefs), and I focus more on the topic that interests me and less on the original topic. And if someone doesn’t notice the switch, they can get confused.

Wow — this reason was in another blind spot for me.

Is everything a function in Haskell?

conal, that’s a funny one. in Haskell, static types give a pretty sharp objective determination of what is a function and what isn’t

Haskell has a precisely specified type system that defines what it means for (the value of) an expression to have function type.

Reasons vs rationalizations

Most of our so-called reasoning consists in finding arguments for going on believing as we already do. (James Harvey Robinson)

Although I keep hearing “everything is a function” (and 3 is a nullary or constant function), I don’t hear people say “everything is a list”, and 3 is really the singleton list [3]. Or “everything is a pair”, and 7 is really (7,⊥) or some such. Or “everything is a Maybe“, and True is really Just True. Personally I don’t like to equate non-functions (number, bools, trees, etc) with 0-ary functions any more than I to equate them with singleton lists (or trees, …) or non-Nothing Maybe values, etc.

So my best guess is that a statement like “well, 3 is a constant function” is not so much a reason as a rationalization. In other words, not so much a means of arriving at a belief as a means of holding onto belief in the face of evidence to the contrary. After all, one could use the same style of “reasoning” to arrive at quite different beliefs, namely that 3 is a list, pair, or Maybe in Haskell. On the other hand, if the task at hand is to solidify a given belief rather than arrive at an understanding, then one would be less likely to notice that a given rationalization can be easily adapted to alternative beliefs.

53 Comments

  1. david:

    we need to be more careful in educating about functions, it appears. It’s a level of abstraction problem, for one thing. Most people like to abstract to the same level and type, it seems in my experience. “Lazy execution”

  2. SLi:

    “Would making everything a function really the formal system that is Haskell programming?”

    I think this sentence no verb. Would making everything a function … simplify?

  3. James Iry:

    I wonder if this is somehow related to the common claims that Scheme, SML, Clojure, or Erlang are purely functional. E.g. I wonder if the speaker makes the connection that purely object oriented language = everything is an OO object therefore a purely functional language = nothing is an OO object.

  4. conal:

    SLi: Thanks for the catch. Yes, the missing word was “simplify”. Now added.

  5. Alex Stangl:

    The disconnect may be related to coming to Haskell from a background in imperative languages like C or C++. In these imperative languages you have data, essentially a bit of memory with a type; and conversely you have “functions” which are blocks of code that take 0 or more arguments and return 0 or more values.

    Haskell newbies probably don’t have a problem with simple (primitive?) values like a = 3 :: Int, because it looks like something they are used to. When they see something that looks like a rather complicated piece of code, even if it is of type Int, they naturally map it to the closest thing they are used to, a “function” that takes 0 arguments and returns an Int. After frustration trying to pigeonhole things as data (values) versus code (functions), it’s not surprising some would go to a simpler “everything is a function” model.

    Possibly I just forgot, but when learning Haskell I don’t remember seeing anything that clarified the essential distinction between the mathematical/Haskell use of the word function, and the non-mathematical/imperative use of the word function. I sent in comments about the Haskell 2010 Report recently, that the word function is misused several places in section 7.1. So sloppiness in the use of word function may not be confined to newcomers.

  6. Andre Pang:

    So if “f = 7″ is neither a nullary function nor a constant, what do you call it? Or is your point that we don’t need to give it a name (which I disagree with)?

  7. Matthew Sellers:

    My question is what is the value of the distinction to the new Haskell programmer?

    When learning a programming language, all distinctions require justification to be considered important.

    If the distinction is not highlighted then it will be overlooked for things that seem more important, like writing Monad tutorials.

  8. conal:

    Andre: I call “f = 7” a definition.

  9. conal:

    My question is what is the value of the distinction to the new Haskell programmer?

    I guess the distinction you mean is between functions and non-functions. Similarly, what value do we get from distinguishing integers from non-integers, pairs from non-pairs, lists from non-lists, etc.? Or, put more generally, of what value are types?

  10. Ivan Lazar Miljenovic:

    I’ve been guilty of this in some aspects, but up-front about it: for SourceGraph, I tend to just group functions and top-level values together as “functions” (though I’m slowly switching to referring to them as “normal entities”) as in terms of a call graph there is no real distinction.

  11. conal:

    I tend to just group functions and top-level values together as “functions” …

    Ivan: By “functions and top-level values”, do you mean simply “top-level definitions” (of both functions and non-functions, i.e., having both function and non-function types).

  12. Ivan Lazar Miljenovic:

    Yes, I do. The terminology is in part to distinguish them from type-class and data type definitions and class instantiations.

  13. Tom Crockett:

    From a categorical perspective, one can say that a type A is the same as the nullary function type () -> A, up to isomorphism. In fact, this morphism is the only way we can talk about “elements” of A in category theory. Every type A is also isomorphic to ((), A) and (A, ()), of course. But this is not true for Maybe and List; there’s only a one-way bijection from an arbitrary A to Maybe[A] or List[A]. I don’t see any particular advantage in thinking of everything as a function, but it doesn’t seem inherently unsound.

  14. Tom Crockett:

    I guess I should be paying more attention to the “in Haskell” part. There is definitely a distinction in Haskell, because A can’t be used interchangeably with () -> A.

  15. Alex Stangl:

    @Matthew Sellers: If you are referring to the distinction between the 2 uses of the word function that I mentioned, the value in learning the distinction is to move up the learning curve and start speaking a common language where we have a shared understanding of what certain words mean. How can we effectively communicate and learn otherwise?

    I used to use the word functor a lot to describe function pointers, function objects, etc. in C, C++, and Java. Now that I have a better appreciation of the mathematical meaning of functor, I no longer use the word in that fashion, and try to clean up my old code when possible, substituting a more appropriate term.

  16. conal:

    Tom (Crockett): Exactly. Your remarks about the categorical perspective are what I referred to above as “Interested in a different conversation”, which you noticed and addressed in your second comment, addressing the “in Haskell” part of the post topic.

  17. conal:

    Ivan: Thanks. For clarity & precision, I suggest revising your vocabulary, along the lines I mentioned. To call a top-level function-typed definition simply a “function” is to mix up two very different notions. Consider that, unlike C/C++, in functional languages many functions are expressed outside of definitions and are expressed in a lexically nested context (which is terrifically useful). Also, Haskell has no “top-level values”, since a module consists of definitions, not expressions. Even in a (possibly top-level) definition like “seven = 3+4“, the expression “3 + 4” is not top-level; it is nested within a definition.

    There is an alternate view, in which an entire module is something like a “top-level value”. I doubt that view is what you have in mind, and I don’t think Haskell’s type system is quite rich enough to get there. (See “first-class modules”.)

    In case anyone wonders why I care about people using language so precisely: it’s because we think in language. Fuzzy speaking is fuzzy thinking.

  18. Tom Lokhorst:

    I thing a big part of the reason I thought “everything is a function” was types.

    When I first saw Int -> Int and learned that this was the type of a function that takes an int and returns an int, I immediately mapped that to int foo(int x) in my imperative mind. Then when I saw Int -> Int -> Int I got confused, that looked like two functions. However, I was told that while these are technically two functions, trough currying I could think of them as one function with two arguments (in my mind int foo(int x, int y)).

    After learning about types, I learned about laziness. In Haskell bar (2 + 3) doesn’t mean that bar gets called with the value 5, but with ‘something’ that eventually evaluates to 5. This is behaviour I had sometimes wanted in my imperative programming, and I always implemented that with a anonymous function in JavaScript.

    Then, when I learned that (2 + 3) has the type Int, I made my final imperative connection: int foo()

  19. Slom:

    When you include ‘type functions’ from system F in your definition of function and explain (GHC) Haskell (as is often done) by translation into a core language based on system F, then it does indeed make sense to talk about the literal ‘3’ as a function (in the core language) from type to value:

    Prelude> :set -XRankNTypes
    Prelude> (3 :: forall t . (Num t) => t)
    3
    

    Also note that the core of GHC is moving from system F into the teritory of dependend types recently (GADTs) where exactly this difference types and values is removed.

  20. Tom Crockett:

    Interestingly, in Scala one can achieve something like “everything is a function” via implicit conversions.

    implicit def foo[A](a:A):Unit=>A = {u => a}

    scala> 1() res6: Int = 1

    scala> 1()() res7: Int = 1

    scala> 1()()() res8: Int = 1

    scala> true() res9: Boolean = true

    scala> true()() res10: Boolean = true

  21. Miguel:

    After reading some of the comments in the reddit discussion I think there is a confusion regarding the categorical semantics of expressions and types. In that setting, the denotation of an expression is a morphism from the denotation of the typing context to the denotation of its type. Hence only closed expressions are denoted by morphisms from the terminal object (i.e., the denotation of the empty context), in fact one should be a little more carefully here and say that for every constant there exists a morphism from the terminal object to the object denoting the type of the constant.

  22. slom:

    I guess my last post slightly missed the topic, sry :(

    I think the problem with that believe is that nobody specifies what ‘everything’, ‘is’ and ‘function’ stand for in the sentence ‘everything is a function’.

    I believe that there are some definitions of these words where the sentence is true.

    interesting question I asked myself, btw: does the expression (+) denote a function in the mathematical sense (always taking same inputs to same outputs)? “The finite-precision integer type Int covers AT LEAST the range [ – 2^29, 2^29 – 1].”

  23. Lars Lundgren:

    “3 is a nullary or constant function”

    I think this can be understood from the associativity of -> and how n-ary functions are encoded with n arrows

    a: Int -> Int # arity 1. 1 arrow b: Int -> Int -> Int # arity 2 (not really, but still a useful mental model). 2 arrows c: Int # Same pattern. Number of arrows: 0. function with arity 0?

  24. conal:

    Several folks have pointed out that, like binary & ternary functions, the idea of “nullary functions” is part of an informal perspective that may sometimes aid intuition.

    Perhaps confusion arises when one person is assuming a formal/precise perspective and another assumes an informal/intuitive perspective.

    So perhaps a helpful response to the question “Is everything a function in Haskell?” is “Not really, but some people find it helpful to informally think of non-functions as nullary functions, just as thinking of curried functions as binary, ternary, etc.” Or, more succinctly, “Yes, but not really.”

    A maybe even more helpful response could be “Do you mean does everything in Haskell have a function type?” Then the asker can get more clear about what s/he wants to know.

    And these responses are easily adapted to be responses when someone states as fact that “Everything is a function” in Haskell (or modern FP in general).

  25. conal:

    interesting question I asked myself, btw: does the expression (+) denote a function in the mathematical sense (always taking same inputs to same outputs)? “The finite-precision integer type Int covers AT LEAST the range [ – 2^29, 2^29 – 1].”

    I was sadly disillusioned about Haskell when I learned that the Int has no implementation-independent semantics. See Notions of purity in Haskell.

  26. Steve:

    Not every recursive data type can be easily encoded as a function. The type constructor newtype Mu f=Mu (forall a.(f a->a)->a) can’t encode non regular types like: data Node a=Two a|Three a data Tree a=Unit a|Up (Tree(Node a))

    Some things aren’t functions, or at least take a lot of effort to make functions.

  27. Steven:

    Non regular data types are difficult or impossible to encode as functions.

  28. conal:

    Steven: do you have any references that prove the impossibility (or difficulty) of encoding non-regular types as functions?

  29. Neel Krishnaswami:

    A thunk at the operational level (language implementation) is vaguely reminiscent of a function at the level of language specification and semantics. This bit of operational reasoning had not occurred to me as a source of thinking than even non-functions are “functions”. I was looking for denotational reasons, i.e., what things mean rather than how an implementation might work.

    This can be said in purely denotational terms, also. Basically, idealized Haskell’s denotational semantics (ie, the category of pointed domains and nonstrict continuous functions) lets you interpret every closed term of type A as a morphism of type 1 → A. Now, since 1 is terminal, we can also interpret any closed term as an element of the domain A. Now, observe that this interpretation works for every Haskell term, and that elements of A are not values, but rather computations. So the denotational semantics offers no distinguished subclass of expressions we can identify as “values”.

    Since most programmers’ only experience of computations is as the body of functions, it seems like a reasonable (if technically inaccurate) for them to say something like “every expression is a function”.

    (OTOH, if you work with predomains, and turn nontermination into an effect via the lift monad, now you can distinguish between values (predomains) and computations. Such languages are even further from common practice, even though they are much more fun to program in….)

  30. weakish:

    Well, “everything is a function” really means “everything is a function except…”. People tends to omit the “except” part.

    Just like “everything is an object”. Again, people omit the “except” part. Even in smalltalk, variables are not objects. A lot of other OOP languages have more exceptions, (e.g. Ruby’s block is not an object, though smalltalk’s block is an object) but they still say “everything is an object”.

  31. conal:

    @weakish:

    Well, “everything is a function” really means “everything is a function except…”. People tends to omit the “except” part.

    Everything except what? Non-functions?

    Even in smalltalk, variables are not objects.

    Aren’t smalltalk variables syntactic entities? In contrast, objects & values are semantic entities.

  32. weakish:

    @conal:

    Yes, everything is X, except non-X. :-) Well, I mean, sometimes people say “everything is X”, but they really mean “(almost) everything is X”. It’s kind of emphasis. (And maybe later another group of people heard this, but failed to understand the omitted “almost/except” part.)

    And thanks for pointing out my confusion of syntactic and semantic. In fact, a ruby user may argue that block is a syntactic entity, and does not evaluate a value.

    P.S. This reminds me that maybe syntactic similarity contributes to the mixing up of functions and definitions. They looks similar to new comers, for example: f = -3 with f = (-)3.

  33. conal:

    Hm. So is almost everything in Haskell a function? I don’t know how even to make the question precise.

  34. Vag:

    “So is almost everything in Haskell a function? I don’t know how even to make the question precise”

    ==> “When you’re looking at typical Haskell program, most definitions are definitions of a functions”.

  35. conal:

    Vag: If I follow you, you’re shifting the question from (nearly) every thing being a function to (nearly) every definition being a function definition. I suspect you shifted the question even further, to (nearly) every top-level definition being a function definition, since I doubt almost all nested definitions are of functions. By “top-level”, I mean directly within a module’s outer-most “where”, rather than a “where” or “let” nested more deeply.

    I’m further guessing that you made this shift unconsciously. Are my guesses correct here? If so, do you have any insight about how the shift happened?

    If you make this same measurement on a typical C program, won’t you come up with the same conclusion — that almost every top-level definition is of a function?

  36. Vag:

    you’re shifting the question … I’m further guessing that you made this shift unconsciously.

    No.

    The gist of my translation proposal is that some people use word “language” to denote rather arbitrary notions, for instance programs or associated activities. Also, I do not see any other possible meanings of “almost everything in Haskell is a function” phrase.

    I doubt almost all nested definitions are of functions.

    I stated about all definitions but now I realize that was mistake, so I restrict my statement to top level definitions.

    If you make this same measurement on a typical C program, won’t you come up with the same conclusion — that almost every top-level definition is of a function?

    Yes, indeed. But that may be passed unnoticed by users of EverythingIsA phrases.

  37. conal:

    I do not see any other possible meanings of “almost everything in Haskell is a function” phrase.

    The first interpretation that occurred to me was that (almost) every expression in (type-correct) Haskell programs denotes a function. Expressions, not definitions, denote things (values).

  38. Vag:

    Most programmers I’m looking at do not know anything about semantics. They learn languages very partially, by example, by trial and error, remember pairs (situation,action), looking over their friend’s shoulder and reading emotional blog posts. Their models is built from enormously dissimilar kind of bricks than yours. Their concepts are full of inadequate or even meaningless vivid images (e.g. “imperative is how to do, declarative is what to do“). They think about programs by imagining actions and tracing — in your language it will be called nontotal operational thinking muddled with lot of implementation details. In the light of this, it is not relevant what is denoted by what and what domain of discourse is in your system of knowledge.

    I do not know is my explanation applicable to all uses of phrase “everything is a function”, but there definitely exist occurances where it applies well.

  39. Ameth:

    When I thought that myself a while ago it was because I realised that in Haskell int x; int y = 5 + x; and int x; int y(){ return 5 + x; } is equivalent and Haskell makes no distinction between them. In that imperative sense of the word function everything really is a one, but in a purely functional context it’s of course wrong.

  40. Eyal Lotem:

    I am reading “Haskell School of Expression” (I’m considering using it as learning material for teaching beginners), and it also says (from memory, not exact quote): “You can think of values as functions of 0 arguments”.

    So this may be another source for this.

  41. Trinithis:

    Your comment about making everything a list or a tuple brings to mind what I learned in a set theory class. In the class I took, everything is a set: functions, numbers, tuples, lists, etc. So I don’t think it is unreasonable to make things out of lists or tuples.

    As for 7 being a function, I think it could work using similar thinking. Whether or not such thinking gets you anywhere is up to debate though.

  42. conal:

    … In the class I took, everything is a set: functions, numbers, tuples, lists, etc. So I don’t think it is unreasonable to make things out of lists or tuples. …

    Trinithis: I’m very sympathetic to your perspective here. And I understand it to be in the category I called “Interested in a different conversation” above:

    This one was the hardest of all for me to get so far and was a real lightbulb moment for some people’s belief that everything is a function in Haskell. They don’t believe it. They’d rather talk about something else, such as formalisms in which everything is a function. Or maybe they even have a different notion of what topic is being discussed.

    Which is not at all to say that the alternative topic is any less important than the original one.

  43. Russ:

    I feel like even authoritative Haskell materials sometimes phrase things in a way that conflate functions with non-functions. For example, in the Haskell Report section 3.1: “The Prelude provides two functions to directly cause such errors: error :: String -> a \ undefined :: a” Of course these are unusual “functions”, but since the type signature of undefined doesn’t include an arrow it seems like it wouldn’t be quite correct to call it a function.

    It seems that the Haskell Report distinguishes data constructors from functions, but yet they are generally indistinguishable aside from deconstruction in pattern matching, e.g. if I type (“:t (:)”) it appears to be a normal function. Yet section 3.6 of the report mentions “where True and False are the two nullary constructors from the type Bool”, which makes it sound like data constructors can be thought of as functions, and ones with no arguments are just nullary functions.

    Maybe the source of the problem is that AFAICT there’s no good word to refer to “a single-word expression, either a variable, function, operator, or data constructor”. In most contexts, those things all act the same. To say “value” isn’t really correct, as that refers to the underlying result of an evaluation. To say “expression” doesn’t make it clear that we’re talking about a single word.

  44. conal:

    Russ: the undefined example is tricky. In general, we might say that “foo is a function” exactly if foo has function type, i.e., foo :: a -> b for some types a and b. In that case, undefined passes the test, since undefined :: a -> b not only for some types but for all types a and b. Of course by this same line of thought, undefined “is a” number, list, pair, boolean, etc. However, I’m guessing that the section you quoted about error and undefined was simply careless writing.

    I see what you mean about “nullary constructor” reinforcing the idea of “everything is a function in Haskell”.

    As for a single-word expression, we do have “identifier”. But it wouldn’t work very well in your example: “The Prelude provides two identifiers to directly cause such errors: …”. Identifiers themselves cannot cause errors. It’s really the values bound to these two identifiers. Here are some other attempts and criticisms:

    • “The Prelude defines two identifiers to directly cause such errors: …”. Vague relationship between identifiers and causality.
    • “The Prelude defines two values to directly cause such errors: …”. Values cannot be defined. Only names.
    • “The Prelude provides two definitions for directly causing such errors: …”. Again, definitions do not cause anything.
    • “The Prelude names two values that directly cause such errors: …”. Most accurate in my comprehension.

    I hadn’t realized that the Haskell report itself was encouraging the sort of confusion I keep noticing. Thanks for pointing out these statements. Now I wonder whether we’ve even developed careful enough language to say what we mean here.

    As Bertrand Russell said, “Everything is vague to a degree you do not realize till you have tried to make it precise.”

  45. Petr Pudlak:

    I read this very interesting post, but what I really missed is a formal definition of what you call a function here. How can we discuss if something (everything) is a function, if we don’t have a formal definition of it? If someone defines that a function is any Haskell expression then everything is a function. If someone defines that a function is a Haskell expression of type σ→τ for some σ and τ then of course not everything is a function.

  46. Paul Higham:

    What I learned in high school can be succinctly stated, somewhat precisely as follows:

    Let X and Y be sets and let the cartesian product of X and Y, denoted X x Y be the set {(x,y) | x belongs to X, y belongs to Y}. A function from X to Y, written as f : X -> Y, is a subset of X x Y with the property that for all (x1,y1), (x2,y2) in f, x1 = x2 implies that y1 = y2.

    This is the mathematical definition of of a function and is the one used by Haskell and probably the other functional programming languages as well. The defining property of the function as stated above is usually referred to as ‘referential transparency’. The C programming language started the confusion, and other such languages have perpetuated it, by using the term function to mean something quite different. In C a function is not necessarily an association at all – it may not take an input and it may not produce an output – it may be pure side effect and even if it does define an association there is no guarantee of referential transparency.

    Adopting this definition, how do you make sense of the phrase ‘a function with 0 arguments’?

  47. Peter LeFanu Lumsdaine:

    As you say, the position that “everything is a function (to the exclusion of being anything else)”, or more specifically “everything is of some function type A -> B”, is clearly wrong.

    However, the viewpoint “everything can be seen a function (in addition to whatever else it is)” seems quite reasonable. As others have pointed out, n-ary (curried) functions are a very helpful concept, where formally an n-ary function can be defined as a term of type “A_1 -> … -> A_n -> B”, for some types A_1 … A…n. So “map” can be seen both as a unary function from “A -> B” to “[A] -> [B]”, and also as a binary function from “A -> B” and “[A]” to “[B]”.

    But then by the same token, the integer 5 is a nullary function to “Int”; it’s also still an “Int”, but that doesn’t stop it being a nullary function as well.

    So “everything is an n-ary function, for some n” is surely acceptable? In the context of Haskell, I don’t know how useful it is; but in some mathematical contexts, it’s very fruitful indeed (e.g. the insight that constants could be seen as 0-ary functions was essential for universal algebra, and its cousin the theory of operads).

  48. Nathan Stoddard:

    When I first learned Haskell I thought everything was a function, mainly because the syntax is so similar. If f x y = 5 is a 2-argument function, and f x = 5 is a 1-argument function, I thought f = 5 must be a 0-argument function. The syntax makes it hard to tell that all functions take exactly one argument.

  49. conal:

    Hi Nathan. It looks like your confusion is as described in “Mixing up functions and definitions” in the post above. Of the three examples you gave, none is a function. Rather, they’re all definitions, which is why the syntax is consistent. The presence of formal parameters in the first two example tells you that a function is getting defined.

  50. Dylan Just:

    The OO-ers can be comforted with the fact that in Haskell, everything is a value… and that just happens to include constants and functions. That can help them bridge their OO knowledge to the functional world.

    Of course, in most “OO” languages, everything is not an object (e.g. methods and primitives in some languages), and in Haskell everything is not a value (modules and type class instances). But, close enough :)

  51. paldepind:

    In the brilliant book The Haskell School of Music Paul Hudak writes: “[…] named quantities […] can be though of as functions with no arguments”. So maybe this way of thinking has also partly originated from Haskell authorities promoting it.

  52. Robert Monfera:

    @paldepind The concept of “named quantities […] can be though of as functions with no argument” goes back, including array languages. I think in k (or q, I forgot) arrays also behave as functions, indices taking the role of arguments. Perhaps such ideas come from APL or FP/FFP.

  53. Tucker:

    I always thought that when people said “everything is a function in Haskell”, what they actually meant was “many things that don’t appear to be functions in other languages appear to be functions in Haskell”.

    Examples:

    In Haskell, constructors and field names can be called like functions (they effectively are functions when used within specific contexts in the code, though they can also be used in other contexts where normal functions can’t).

    In other languages, operators are built into the language and are distinct from functions. In Haskell, operators are just functions with a special syntax. (They’re actually variables, but they usually contain function values.)

    In Javascript (for example), we can have: x = 5 function f() { return 5 } We can get 5 from either of these by saying ‘x’ or ‘f()’ respectively. The equivalent in Haskell is: x = 5 f = 5 And we can get 5 from either by saying ‘x’ or ‘f’ respectively. In the former case, a function that takes no arguments is distinct from a variable with a value. In the latter case, it is not. I think this thought process comes from a number of misunderstandings about the language: – They see a definition without an argument as a zero-arity function (as opposed to what’s actually happening: a definition with an argument is a variable, but the value of that variable is a function value). – Similarly, they see a definition with multiple arguments as a multi-arity function, as opposed to a 1-arity function that returns another function. – They see type syntax as a list of function argument types and a return type separated by arrows, so they figure if there’s no arrows it’s a function that just returns something and takes no arguments. – They’re probably coming from languages with no lambdas, so they don’t really understand the concept of “a variable with a function value”, and instead think of functions as a special kind of toplevel definition with a special syntax. So, when they see toplevel definitions of functions in Haskell, they conclude that all toplevel definitions are functions (since they aren’t syntactically distinct). I think this is what you mean when you talk about “definition vs function”. In my mind, this can actually be a useful (or at least non-harmful) way to think about it; a constant variable can be thought of as a zero-arity function, just as a multi-argument definition can be thought of a multi-arity function, and just as a value with parentheses around it can be thought of as a 1-tuple. None of these things are actually true, but the language is designed in such a way as to allow you to program as if they were true, for the most part.

    I’m sure there are other examples that escape me at the moment. The point is, I expect people come into Haskell and read about functions, and then they read about constructors and think “oh those are functions”, and then they read about operators and think “oh those are functions too”, and then they read about variable definitions and think “those definitely also look like functions … man, it really seems like everything in Haskell is a function!”

Leave a comment