Search code examples
purescript

PureScript - What is a newtype?


The PureScript handbook defines an exercise as follows:

Define a Show instance for Point. Match the same output as the showPoint function from the previous chapter. Note: Point is now a newtype (instead of a type synonym), which allows us to customize how to show it. Otherwise, we'd be stuck with the default Show instance for records. (https://book.purescript.org/chapter6.html)

However, no information about what a newtype is is provided.

What is a newtype? What is it for? Why does it work here, but not type?


Solution

  • Newtype is just a wrapper around one(or more) type. We usually use it to expose a predefined or already available type as a new type.

    There are lot of useful things we can do with newtype, one of them is specified by the author in the chapter you've shared above.

    When you specify

    type Point = 
      { x :: Number
      , y :: Number
      }
    

    You can't actually define your own type class instances(e.g show instance) as Point is just a type synonym for Record type which is predefined in Prim module of purescript.

    So you wrap it with newtype, and make it a user defined type. Now you can derive whatever instance you want.

    You can also use it to create more strict values. For example, you want to create a type name, when its size is less than 6, you want to emphasize a error. So what you do is,

    type Name = Either String String
    

    then you create a function,

    type Name = Either String String 
    createName' :: String -> Name 
    createName' name 
     | (length name) >= 6 =   (Right name)
     | otherwise          =   (Left "Invalid Name")
    

    you're happy now. But this doesn't restrict the other developer from doing something like

     myName :: Name
     myName = (Right "a")
    

    So how you can let the Name to be created only by the function?

    1. By Only importing the function.
    2. By not importing the Name

    The problem is you can't do 2nd step, as Name is just a type synonymn.

    name :: Name is same as name :: Either String String

    so you wrap the Either String String with a newtype

     newtype StrictName = MkName (Either String String)
     createName :: String -> StrictName 
     createName name 
        |  (length name) >= 6 =  MkName (Right name)
        |  otherwise          =  MkName (Left "Invalid Name")
    

    now you only import the function and the StrictName(without its construtor) e.g

    module Data.MyTypes.Name
    (StrictName,createName)
    

    Now there's no other way to create a StrictName value without using the createName function. (this is also doable with ADT types, but I prefer newtype)

    Newtype are also used to handle orphan instances and many more useful things.

    Note : Newtype adds runtimes overhead as theres one more wrapper around the original type.