Permalink
Switch branches/tags
Nothing to show
Find file Copy path
167 lines (108 sloc) 6.61 KB

Type Classes

PureScript supports type classes via the class and instance keywords.

Types appearing in class instances must be of the form String, Number, Boolean, or C t1 ... tn where C is a type constructor (including -> and t_i are types of the same form).

Here is an example of the Show typeclass, with instances for String, Boolean and Array:

class Show a where
  show :: a -> String

instance showString :: Show String where
  show s = s

instance showBoolean :: Show Boolean where
  show true = "true"
  show false = "false"

instance showArray :: (Show a) => Show (Array a) where
  show xs = "[" <> joinWith ", " (map show xs) <> "]"

example = show [true, false]

Overlapping instances are no longer allowed in PureScript. To write overlapping instances, you should use Instance Chains.

Instance Chains

PureScript implements a form of instance chains that work on groups of instances matching by parameters. This means that constraints are not considered when choosing instances. However, you can still write a chain of instances in consecutive order that will be matched top to bottom by using the else keyword.

Here is an example of a MyShow typeclass, with instances for String, Boolean, and any other type.

class MyShow a where
  myShow :: a -> String

instance showString :: MyShow String where
  myShow s = s

else instance showBoolean :: MyShow Boolean where
  myShow true = "true"
  myShow false = "false"

else instance showA :: MyShow a where
  myShow _ = "Invalid"

data MysteryItem = MysteryItem

main = do
  log $ myShow "hello" -- hello
  log $ myShow true -- true
  log $ myShow MysteryItem -- Invalid

Multi-Parameter Type Classes

TODO. For now, see the section in PureScript by Example.

Superclasses

Superclass implications can be indicated in a class declaration with a backwards fat arrow <=:

class (Monad m) <= MonadFail m where
  fail :: forall a. String -> m a

This code example defines a MonadFail class with a Monad superclass: any type which defines an instance of MonadFail will be required to define an instance of Monad too.

Superclass instances will be used when searching for an instance of a subclass. For example, in the code below, the Applicative constraint introduced by the pure function can be discharged since Applicative is a superclass of Monad, which is in turn a superclass of MonadFail:

assert :: forall m. (MonadFail m) => Boolean -> m Unit
assert true = pure unit
assert false = fail "Assertion failed"

Orphan Instances

Type class instances which are defined outside of both the module which defined the class and the module which defined the type are called orphan instances. Some programming languages (including Haskell) allow orphan instances with a warning, but in PureScript, they are forbidden. Any attempt to define an orphan instance in PureScript will mean that your program does not pass type checking.

For example, the Semigroup type class is defined in the module Data.Semigroup, and the Int type is defined in the module Prim. If we attempt to define a Semigroup Int instance like this:

module MyModule where

import Prelude

instance semigroupInt :: Semigroup Int where
  append = (+)

This will fail, because semigroupInt is an orphan instance. You can use a newtype to get around this:

module MyModule where

import Prelude

newtype AddInt = AddInt Int

instance semigroupAddInt :: Semigroup AddInt where
  append (AddInt x) (AddInt y) = AddInt (x + y)

In fact, a type similar to this AddInt is provided in Data.Monoid.Additive, in the monoid package.

For multi-parameter type classes, the orphan instance check requires that the instance is either in the same module as the class, or the same module as at least one of the types occurring in the instance. (TODO: example)

Functional Dependencies

TODO. For now, see the section in PureScript by Example.

Type Class Deriving

Some type class instances can be derived automatically by the PureScript compiler. To derive a type class instance, use the derive instance keywords:

newtype Person = Person { name :: String, age :: Int }

derive instance eqPerson :: Eq Person
derive instance ordPerson :: Ord Person

Currently, the following type classes can be derived:

Compiler-Solvable Type Classes

Some type classes can be automatically solved by the PureScript Compiler without requiring you place a PureScript statement, like derive instance, in your source code.

foo :: forall t. (Warn "Custom warning message") => t -> t
foo x = x

Automatically solved type classes are included in the Prim modules:

Symbol-related classes

Prim.Row

Prim.RowList

Other classes