02-Instances-for-Newtyped-Types.purs

module Syntax.Basic.Deriving.NewtypedTypes where

import Prelude

-- License applies to a portion of this document --> Start

-- Original credit: @paf31
-- Link: https://github.com/paf31/24-days-of-purescript-2016/blob/master/4.markdown
-- Changes made: 
-- - use meta-language to explain newtype typeclass derivation syntax
-- - added example of `Box a`
-- - added `example1` and `example2`
--
-- Licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
--   https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US

-- Newtypes are used to wrap existing types with more information around them
newtype EmailAddress1 = EmailAddress1 String

-- However, defining type class instances for them
-- can get tedious:

instance Eq EmailAddress1 where
  eq (EmailAddress1 s1) (EmailAddress1 s2) = s1 == s2

-- same for Ord type class

-- same for other newtypes
newtype Phone = Phone String
newtype FirstName = FirstName String
-- etc...

-- This is boilerplate since we unpack the instance and delegate the function
-- to the wrapped type's implementation. It's also inefficient in terms of
-- evaluation. Purescript gives us a way to derive, for newtypes, any instance
-- the boxed type implements, using the following syntax.
-- We use 'derive newtype' in front of the instance:

newtype EmailAddress2 = EmailAddress2 String

derive newtype instance Eq EmailAddress2
derive newtype instance Eq Phone
derive newtype instance Eq FirstName

-- And now we can use it:
example1 :: EmailAddress2 -> EmailAddress2 -> Boolean
example1 email1 email2 = email1 == email2

-- If we have a type that takes a type paramter (i.e. the `a`)
data Box a = Box a

derive instance (Eq a) => Eq (Box a)

-- and we newtype that value
newtype SpecialBox a = SpecialBox (Box a)

-- then we need to add the `Eq a` constraint
-- before it will compile.
derive newtype instance (Eq a) => Eq (SpecialBox a)

example2 :: SpecialBox Int -> SpecialBox Int -> Boolean
example2 a b = a == b

-- <--- End