Data declaration without a data constructor. Can it be created? Why does it compile?
While reading one of my Haskell books, I came across a suggestion:
Data declarations always create a new type of constructor, but they may or may not create new data constructors.
It was strange to be able to declare a data type without a data constructor, because it seems that then you can never instantiate the type. So I tried it. The following data declaration compiles without errors.
data B = String
How do I create an instance of this type? Is it possible? I cannot find a way.
I thought that a data constructor with a name that matches the type constructor would be generated automatically, but that doesn't look like the error shown by trying to use B
as a scoped data constructor.
Prelude> data B = String deriving Show
Prelude> B
<interactive>:129:1: error: Data constructor not in scope: B
Why is this data declaration allowed to compile if the type can never be created? Is this allowed solely for some formal reason, despite the fact that it has no known practical application?
I also wonder if my book statement on non-constructor data types can refer to types declared with type
or newtype
rather than data
.
-
In the case,
type
type synonyms do not explicitly use these constructors, as shown below.Prelude> type B = String Prelude>
Type synonyms such as this can be created by the constructors of the type they are set to. But I'm not sure if this is what the book is referring to as type synonyms, it doesn't seem to declare a new data type as much as just defining a new alias for an existing type.
-
In case it
newtype
appears that types without data, constructors cannot be created, as shown by the following error.Prelude> newtype B = String <interactive>:132:13: error: β’ The constructor of a newtype must have exactly one field but βStringβ has none β’ In the definition of data constructor βStringβ In the newtype declaration for βBβ
type
and newtype
it doesn't seem to be relevant to what the book is talking about, which brings me back to my original question: why is it possible to declare a type using data
without a data constructor?
source to share
How do I create an instance of this type?
The statement from your book is correct, but your example is not. data B = String
defines a type constructor B
and a data constructor String
, both with no arguments. Note that the String
one you are defining is in the value namespace, therefore different from the normal type constructor String
.
ghci> data B = String
ghci> x = String
ghci> :t x
x :: B
However, here is an example of defining data without data constructors (so it cannot be created).
ghci> data B
Now I have a new type constructor B
, but no data constructors to create type values B
. In fact, this datatype is declared in Haskell base
: it's called Void
:
ghci> import Data.Void
ghci> :i Void
data Void -- Defined in βData.Voidβ
Why is this data declaration allowed to compile if the type can never be created?
The ability to have uninhabited types comes in handy in several places. The examples I can think of now are mostly passed by a type such as a type parameter to another type constructor. Another practical use case is in streaming libraries such as conduit
.
There is a type constructor ConduitM i o m r
, where: i
- the type of the elements of the input stream, the o
type of the elements of the output stream, the m
monad in which the actions are performed r
- the final result obtained at the end.
He then defines Sink
as
type Sink i m r = ConduitM i Void m r
since a Sink
should never output any values. Void
is a compile-time guarantee that Sink
it cannot output any (non-bottom) values.
As Identity
, Void
mostly useful in combination with other abstractions.
... type synonyms do not explicitly use data constructors
Yes, but they don't define type constructors either. Synonyms are just a superficial renaming of convenience. Nothing new is defined under the hood.
In the case of the new type, it seems that types without data constructors cannot be created, as shown by the following error.
I suggest you see what for newtype
. The whole point newtype
is to provide a zero cost wrapper around the existing type. This means that you have one and exactly one constructor that takes one and only one argument (the wrapped value). At compile time, the wrapping and reversal operations become NOPs.
source to share