12

I was thinking about pure Object Oriented Languages like Ruby, where everything, including numbers, int, floats, and strings are themselves objects. Is this the same thing with pure functional languages? For example, in Haskell, are Numbers and Strings also functions?

I know Haskell is based on lambda calculus which represents everything, including data and operations, as functions. It would seem logical to me that a "purely functional language" would model everything as a function, as well as keep with the definition that a function most always returns the same output with the same inputs and has no state.

Matt Fenwick
  • 48,199
  • 22
  • 128
  • 192
James Scourtos
  • 691
  • 6
  • 7
  • 7
    *A well-known scientist once gave a public lecture on astronomy. He described how the earth orbits around the sun and how the sun, in turn, orbits around the center of a vast collection of stars called our galaxy. At the end, a little old lady at the back of the room got up and said: "What you have told us is rubbish. The world is really a flat plate supported on the back of a giant tortoise." The scientist gave a superior smile before replying, "What is the tortoise standing on?" "You're very clever, young man, very clever" said the old lady. "But it's turtles all the way down!"* – HostileFork says dont trust SE Nov 27 '12 at 23:33
  • I'd say it strongly depends on what you call a function. Unless we start with a definition what we consider a function, we can hardly talk about what is and what isn't a function. – Petr Nov 28 '12 at 07:02
  • 2
    @HostileFork: Fortunately, it turns out that [turtles use functions too](http://en.wikipedia.org/wiki/Logo_%28programming_language%29). Lambdas all the way down! – C. A. McCann Nov 28 '12 at 17:32

5 Answers5

15

It's okay to think about that theoretically, but...

Just like in Ruby not everything is an object (argument lists, for instance, are not objects), not everything in Haskell is a function.

For more reference, check out this neat post: http://conal.net/blog/posts/everything-is-a-function-in-haskell

wrhall
  • 1,288
  • 1
  • 12
  • 26
  • 2
    Exactly the link I was going to post – luqui Nov 27 '12 at 23:00
  • 6
    I think the analogy to Ruby/purely object oriented languages is a bit flawed since, while not everything in Ruby might be an object, every *value* is an object - which is how the term "purely object oriented" is usually defined. The same is not true in Haskell or purely functional languages in general, i.e. not every value is a function. – sepp2k Nov 27 '12 at 23:32
  • @sepp2k you make a good point about how the comparison is flawed. However, my example of argument lists is a bit contrived. Code blocks also aren't objects but can be wrapped in objects (of the Proc class), so even some things that *could* be objects aren't. For more on Ruby, you could see this answer: http://stackoverflow.com/a/3429889/1572626 – wrhall Nov 27 '12 at 23:40
14

@wrhall gives a good answer. However you are somewhat correct that in the pure lambda calculus it is consistent for everything to be a function, and the language is Turing-complete (capable of expressing any pure computation that Haskell, etc. is).

That gives you some very strange things, since the only thing you can do to anything is to apply it to something else. When do you ever get to observe something? You have some value f and want to know something about it, your only choice is to apply it some value x to get f x, which is another function and the only choice is to apply it to another value y, to get f x y and so on.

Often I interpret the pure lambda calculus as talking about transformations on things that are not functions, but only capable of expressing functions itself. That is, I can make a function (with a bit of Haskelly syntax sugar for recursion & let):

purePlus = \zero succ natCase -> 
   let plus = \m n -> natCase m n (\m' -> plus m' n)
   in plus (succ (succ zero)) (succ (succ zero))

Here I have expressed the computation 2+2 without needing to know that there are such things as non-functions. I simply took what I needed as arguments to the function I was defining, and the values of those arguments could be church encodings or they could be "real" numbers (whatever that means) -- my definition does not care.

And you could think the same thing of Haskell. There is no particular reason to think that there are things which are not functions, nor is there a particular reason to think that everything is a function. But Haskell's type system at least prevents you from applying an argument to a number (anybody thinking about fromInteger right now needs to hold their tongue! :-). In the above interpretation, it is because numbers are not necessarily modeled as functions, so you can't necessarily apply arguments to them.

In case it isn't clear by now, this whole answer has been somewhat of a technical/philosophical digression, and the easy answer to your question is "no, not everything is a function in functional languages". Functions are the things you can apply arguments to, that's all.

luqui
  • 59,485
  • 12
  • 145
  • 204
  • I'm not sure I follow what you're saying by _"But Haskell's type system at least prevents you from applying an argument to a number (anybody thinking about fromInteger right now needs to hold their tongue!"_. Could you expand? – AndrewC Nov 28 '12 at 00:31
  • @luqui: "That gives you some very strange things, since the only thing you can do to anything is to apply it to something else. When do you ever get to observe something?" Actually, one answer to that conundrum would be to add the `IO` monad and some set of `IO` primitives... – Luis Casillas Nov 28 '12 at 01:27
  • @AndrewC, oh, there's just some funny stuff you can do involving `instance Num (a -> b)`, and it's rather tangential and Haskell-specific. – luqui Nov 28 '12 at 07:10
  • Oh wow, ironic that it was me that missed [that](http://stackoverflow.com/questions/12133932/numbers-as-multiplicative-functions-weird-but-entertaining) meaning in what you said! :) – AndrewC Nov 28 '12 at 07:14
  • shouldn't it be `let plus = \m n -> natCase m n (\m' -> plus m' (succ n))`? Also, maybe it's better named `pure_2Plus2`? – Will Ness Dec 09 '12 at 20:15
6

A very different angle on this question: all sorts of data in Haskell can be represented as functions, using a technique called Church encodings. This is a form of inversion of control: instead of passing data to functions that consume it, you hide the data inside a set of closures, and to consume it you pass in callbacks describing what to do with this data.

Any program that uses lists, for example, can be translated into a program that uses functions instead of lists:

-- | A list corresponds to a function of this type:
type ChurchList a r = (a -> r -> r)  --^ how to handle a cons cell
                   -> r              --^ how to handle the empty list
                   -> r              --^ result of processing the list

listToCPS :: [a] -> ChurchList a r
listToCPS xs = \f z -> foldr f z xs

That function is taking a concrete list as its starting point, but that's not necessary. You can build up ChurchList functions out of just pure functions:

-- | The empty 'ChurchList'.
nil :: ChurchList a r
nil = \f z -> z

-- | Add an element at the front of a 'ChurchList'.
cons :: a -> ChurchList a r -> ChurchList a r
cons x xs = \f z -> f z (xs f z)

foldChurchList :: (a -> r -> r) -> r -> ChurchList a r -> r
foldChurchList f z xs = xs f z

mapChurchList :: (a -> b) -> ChurchList a r -> ChurchList b r
mapChurchList f = foldChurchList step nil
    where step x = cons (f x)

filterChurchList :: (a -> Bool) -> ChurchList a r -> ChurchList a r
filterChurchList pred = foldChurchList step nil
    where step x xs = if pred x then cons x xs else xs

That last function uses Bool, but of course we can replace Bool with functions as well:

-- | A Bool can be represented as a function that chooses between two 
-- given alternatives.
type ChurchBool r = r -> r -> r

true, false :: ChurchBool r
true a _ = a
false _ b = b

filterChurchList' :: (a -> ChurchBool r) -> ChurchList a r -> ChurchList a r
filterChurchList' pred = foldChurchList step nil
    where step x xs = pred x (cons x xs) xs

This sort of transformation can be done for basically any type, so in theory, you could get rid of all "value" types in Haskell, and keep only the () type, the (->) and IO type constructors, return and >>= for IO, and a suitable set of IO primitives. This would obviously be hella impractical—and it would perform worse (try writing tailChurchList :: ChurchList a r -> ChurchList a r for a taste).

Vitus
  • 11,822
  • 7
  • 37
  • 64
Luis Casillas
  • 29,802
  • 7
  • 49
  • 102
  • This is a good answer, but it turns out that the types in this encoding are subtly incorrect. Cf. http://stackoverflow.com/questions/6595749/subtraction-of-church-numerals-in-haskell/6596700#6596700 – luqui Nov 28 '12 at 07:07
  • Lovely explanation of how you _could_ theoretically change Haskell from having values as at present to only having functions. – AndrewC Nov 28 '12 at 07:21
  • Why keep `()`? It has a perfectly reasonable Church encoding, namely `id`. – C. A. McCann Nov 28 '12 at 17:23
6

The "pure" in "pure functional" refers to the "freedom from side effects" kind of purity. It has little relation to the meaning of "pure" being used when people talk about a "pure object-oriented language", which simply means that the language manipulates purely (only) in objects.

The reason is that pure-as-in-only is a reasonable distinction to use to classify object-oriented languages, because there are languages like Java and C++, which clearly have values that don't have all that much in common with objects, and there are also languages like Python and Ruby, for which it can be argued that every value is an object1

Whereas for functional languages, there are no practical languages which are "pure functional" in the sense that every value the language can manipulate is a function. It's certainly possible to program in such a language. The most basic versions of the lambda calculus don't have any notion of things that are not functions, but you can still do arbitrary computation with them by coming up with ways of representing the things you want to compute on as functions.2

But while the simplicity and minimalism of the lambda calculus tends to be great for proving things about programming, actually writing substantial programs in such a "raw" programming language is awkward. The function representation of basic things like numbers also tends to be very inefficient to implement on actual physical machines.

But there is a very important distinction between languages that encourage a functional style but allow untracked side effects anywhere, and ones that actually enforce that your functions are "pure" functions (similar to mathematical functions). Object-oriented programming is very strongly wed to the use of impure computations3, so there are no practical object-oriented programming languages that are pure in this sense.

So the "pure" in "pure functional language" means something very different from the "pure" in "pure object-oriented language".4 In each case the "pure vs not pure" distinction is one that is completely uninteresting applied to the other kind of language, so there's no very strong motive to standardise the use of the term.


1 There are corner cases to pick at in all "pure object-oriented" languages that I know of, but that's not really very interesting. It's clear that the object metaphor goes much further in languages in which 1 is an instance of some class, and that class can be sub-classed, than it does in languages in which 1 is something else than an object.

2 All computation is about representation anyway. Computers don't know anything about numbers or anything else. They just have bit-patterns that we use to represent numbers, and operations on bit-patterns that happen to correspond to operations on numbers (because we designed them so that they would).

3 This isn't fundamental either. You could design a "pure" object-oriented language that was pure in this sense. I tend to write most of my OO code to be pure anyway.

4 If this seems obtuse, you might reflect that the terms "functional", "object", and "language" have vastly different meanings in other contexts also.

Ben
  • 68,572
  • 20
  • 126
  • 174
0

Is getChar :: IO Char a function or not? Haskell Report doesn't provide us with a definition. But it states that getChar is a function (see here). (Well, at least we can say that it is a function.)

So I think the answer is YES.

I don't think there can be correct definition of "function" except "everything is a function". (What is "correct definition"? Good question...) Consider the next example:

{-# LANGUAGE NoMonomorphismRestriction #-}
import Control.Applicative

f :: Applicative f => f Int
f = pure 1

g1 :: Maybe Int
g1 = f

g2 :: Int -> Int
g2 = f

Is f a function or datatype? It depends.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Yuras
  • 13,856
  • 1
  • 45
  • 58
  • What about the value `"foo"`? Is that a function? Unless you want to think in terms of zero-arity functions, the answer is straightforwardly "no". And while the concept of zero-arity functions is probably defensible, I don't think it helps; you have to toss out "all Haskell functions take exactly one argument" for example. The question "is `f` a function or a datatype" is a false dichotomy. There is no grand division between values and functions; functions are just a particular kind of value. – Ben Nov 29 '12 at 22:37
  • My take: `g1` is not a function, `g2` is; `f` is a polymorphic value which can be used as a function in particular contexts. Like any polymorphic value, different monomorphic instances of it will have different properties, but you don't have access to any of those when you operate on the "fully general" `f`, because they don't apply to to `f` in all contexts. The property of "being a function" is no more special than "being a list"; `g3 = f :: [Int]` doesn't show that "everything is a list", and neither does `g2` show that "everything is a function". – Ben Nov 29 '12 at 22:41
  • "There is no grand division between values and functions" -- yes, it is what I tried to show using the example)) "Unless you want to think in terms of zero-arity functions, the answer is straightforwardly "no"" -- the answer is "yes" cos HR _uses_ zero-arity-function notation. – Yuras Nov 29 '12 at 22:52
  • What makes you think that Haskell Report treats values as zero-arity functions? If you are judging from the fact that there is `getChar :: IO Char` in the section "Input Functions", may I point you few paragraphs below, where they clearly say "getChar operation", but also "interact function". – Vitus Nov 30 '12 at 00:07
  • Also, in mathematics, we are usually thinking of n-ary functions as Π i∈{1, .., n} Ai → B (where Π is the Cartesian product). The domain of a nullary functon is then the empty Cartesian product, that is {()}. But clearly Haskell makes distinction between `f :: () -> B` and `g :: B` (even though they are isomorphic). – Vitus Nov 30 '12 at 00:10
  • @Vitus How it changes the fact that `getChar` is a function? HR uses also terms "operation", "computation", "value", etc. But it us a function. At least we can say that it is a function. – Yuras Nov 30 '12 at 08:30
  • Haskell Report consistently uses the term "IO operation/computation" for IO actions (that is, `IO a` for some `a`) and "IO function" for, well, IO functions (`a1 -> a2 -> ... an -> IO b`). I could also argue that Haskell Report defines function type to be `type -> type`, in which case `x :: IO a` isn't a function. In any case, there is no rigorous definition of what it means to be a function or value, so your point is moot. – Vitus Nov 30 '12 at 12:22