Asad Iqbal
Asad Iqbal

Reputation: 3321

Haskell newtype definition

I read this statement in the code provided for a homework:-

newtype STR a = STR (Store -> (Result a, Store))

The above link makes it seem like:

a === (Store -> (Result a, Store))

How can this be a valid statement? Does it mean that a is a function which takes Store as argument and returns ('the same function wrapped in Result', Store) ?

Upvotes: 0

Views: 344

Answers (2)

CR Drost
CR Drost

Reputation: 9817

So for data/newtype definitions the left-hand-side holds a type definition including a name and some type variables, while the right hand side holds a constructor list usually also including names and types. An example is:

data List x = Nil | Cons x (List x)

Notice that Nil and Cons are your constructors, x and List x are types of arguments to the Cons constructor, while List is the name of the type (which has kind * -> *; it requires a type argument ___ to "fill it in" before it can describe a list of ___s).

Sometimes we want to alias a type. There are two ways to do that. First, type, which leaves the types commensurate -- so if you write type DirectoryPath = [String] then when you have a DirectoryPath you can manipulate it like a list of strings; in particular you might use : or ++ to append to it; so "apps" : base_directory would be perfectly legal Haskell.

Sometimes, you just want to clobber these functions with a big warning sign, "don't use this if you don't know what you're doing." For that, you might helpfully write data FilePath = FilePath [String]. Notice that we are abusing notation a little bit, naming the type the same as the only constructor for that type. Now to do the same thing you would have to write:

case base_directory of FilePath bd -> FilePath $ "apps" : bd

Why might you do this? Well, first off, in the above syntax the directory structure is growing from right-to-left whereas most people write directories left-to-right. Second, you might want appending a . to be a no-op (i.e. bd ++ []) and .. to be a parent-pointer (i.e. tail bd). You might also have some bizarre conventions (e.g. if the list begins with "" then it's an absolute directory, otherwise it's relative to the present directory) which need to be maintained. Finally, you might want to later shift the code to be Maybe [String] so that a Nothing value could represent paths which do crazy things, like /../.. (absolute path two parents above the root).

All of this becomes easier if you can just say:

FilePath xs ./ x
    | x == "."  = FilePath xs
    | x == ".." = FilePath (tail xs)
    | otherwise = ...

and then enforce that everywhere else people write base_directory ./ "apps".

Another example is newtype SanitizedString = Sanitized String. Since we didn't use type we get a compile time label which traces through our code making sure that user-provided strings are properly escaped before, say, they head into the database insert statements or user interface or wherever.

What you're probably using it for here is that you can write a Monad instance for that type and thereby use it with do-notation.

IF your code only wraps one existing type, newtype avoids introducing additional laziness worries and data constructor delays and so forth. Otherwise it is just like data. So in your code:

newtype STR a = STR (Store -> (Result a, Store))

this is not a type synonym but more like a data constructor which ultimately disappears after compilation. A STR a is an alias for a Store -> (Result a, Store) which was wrapped in a STR constructor (so it can't be used as a function directly without a destructuring assignment).

Upvotes: 3

yatima2975
yatima2975

Reputation: 6610

The newtype definition is a bit confusing, because the symbol STR is used in two different meanings: namely that of type name (the first occurrence) and that of constructor (the second). Renaming both to something different leads to the equivalent

newtype STRType a = STRConstructor (Store -> (Result a, Store))

In other words, this introduces a type STRType a which is structurally the same as Store -> (Result a, Store) (but needs to be wrapped in a STRConstructor)

I hope your course/book already went into the differences between type, data and newtype; otherwise this is going to remain rather mysterious, I'm afraid...

Upvotes: 9

Related Questions