3

I have the following function in my Haskell file:

notFound :: () -> IO ()
notFound = putStr "Sorry, your command could not be found"

That function doesn't compile. This is the compile error I get:

todos.hs:27:12:
    Couldn't match expected type ‘() -> IO ()’ with actual type ‘IO ()’
    Possible cause: ‘putStr’ is applied to too many arguments
    In the expression: putStr "Sorry, your command could not be found"
    In an equation for ‘notFound’:
        notFound = putStr "Sorry, your command could not be found"

But the following function does:

    notFound :: IO ()
    notFound = putStr "Sorry, your command could not be found"

Question:

From my understanding of the type signature, it would seem that these are the same, that () means that no arguments are passed into the function. Is that not the case? If not, what does () in a function signature mean then?

Jorge Silva
  • 4,574
  • 1
  • 23
  • 42
  • hmm... this seems to be a pretty good answer to my question. `()` is a specific type different from not passing anything (duh!). http://stackoverflow.com/questions/29970012/in-function-variable-and-application – Jorge Silva May 21 '16 at 23:40
  • Guess, I'm still not sure when you would do `notFound ()` vs `notFound` to invoke a function – Jorge Silva May 21 '16 at 23:41
  • 1
    does https://stackoverflow.com/questions/33112439/what-does-mean-in-haskell help – hao May 21 '16 at 23:44
  • I guess now I'm curious when I would use `()` in a type signature vs just not using anything? – Jorge Silva May 21 '16 at 23:47
  • 1
    that's a deep question. perhaps it would help to look at all the functions that you've used that have () in the type signature. do you notice any commonality between them? – hao May 21 '16 at 23:49
  • 1
    "the following function [...] `notFound :: IO ()`" - I think perhaps this is the root of your confusion: What you're defining there isn't a function. It's a value of type `IO ()`. The function type is `(->)`. It's a totally different thing. – Chris Martin May 21 '16 at 23:59
  • Means that you have pass a value of type `()` (Unit type) as paramenter and the only value with that type is `()` as well. Meaning of `()`: there is no information here. – David Lilue May 22 '16 at 02:44

2 Answers2

5

() is the empty tuple. It's not nothing, it's more like the void type in C-like languages or a fixed length list of length zero. There's exactly one object with the type: () :: (), unlike something like Bool.

So why have this? Generally, I've seen it used to "close" one of the variables in a type. So IO () is an IO operation which always returns the same thing: ().

A possibly better example would be: Conduit a IO b is something that takes some stream of type a objects and transforms it into a stream of b objects. But what if you have something that doesn't accept an input stream? You can represent it as a Conduit () IO b to "close off" the input stream. (I'm playing loose and fast with the definitions here, but I think it still more or less works)

Michael Klein
  • 319
  • 4
  • 10
  • 1
    Oh, ok, ok. This makes sense. So it's not so much as having `()` as an argument you pass into a function, but more about having a function with side effects RETURN `()`. Right? – Jorge Silva May 22 '16 at 00:01
  • 1
    Yes. A function that has type `f :: () -> a` can easily be resolved to `a` by doing `f () :: a`, but IO is defined as always returning something so you have to put something in the slot. If this still isn't too clear, it could help that `IO a` is defined in the background as a function taking the "state of the real world" and returning the tuple `("[new] state of the real world", a)`. – Michael Klein May 22 '16 at 00:07
  • 1
    Careful with using `Void` – that [usually refers to an *empty* type](http://hackage.haskell.org/package/void-0.7.1/docs/Data-Void.html), i.e. `data Void` with no constructors. (Although yes, `void` return types in C-like languages correspond to `()` and not the empty `Void`.) – Antal Spector-Zabusky May 22 '16 at 00:55
  • 1
    @AntalSpector-Zabusky Great point. I had intended to show similarity, but you're right that it could be confusing. I'll edit. – Michael Klein May 22 '16 at 01:00
1

Your notFound function is defining a function that takes a value of type (), and returns a value of type IO(). () is the only value of type (). Your function is unintentionally in point-free notation

notFound :: () -> IO ()
notFound = putStr "Sorry, your command could not be found"

is equivalent to

notFound :: () -> IO ()
notFound () = putStr "Sorry, your command could not be found" ()

This obviously doesn't work because the function putStrLn does not accept () as a second argument.

You could of course define it as

notFound :: () -> IO ()
notFound () = putStr "Sorry, your command could not be found"

This would compile, but is fairly nonsensical, since it would need to be invoked as notFound ().

What you actually want is just the IO action, not a function. So you can just define it as notFound :: IO().

Free_D
  • 577
  • 3
  • 16