Apples and Oranges
(How Not to Mix Things)

Matej Kollár
mkollar+fpb1@redhat.com

May 2015

We are FPB

So You Were Told…

…that you should not mix apples and oranges…

Everything in a Single Type

module Fruit1 where

data Fruit
    = Apples  Integer
    | Oranges Integer
    deriving Show

mix :: Fruit -> Fruit -> Fruit
mix (Apples m)  (Apples n)  = Apples (m + n)
mix (Oranges m) (Oranges n) = Oranges (m + n)
mix _ _ = error "Not gonna happen!"

main :: IO ()
main = do
    print $ Apples 4  `mix` Apples 5
    print $ Oranges 4 `mix` Oranges 5
    print $ Apples 4  `mix` Oranges 5 -- :-(

Multiple Types

module Fruit2 where

newtype Apples  = Apples  Integer deriving Show
newtype Oranges = Oranges Integer deriving Show

mixApples :: Apples -> Apples -> Apples
Apples m `mixApples` Apples n = Apples (m + n)

mixOranges :: Oranges -> Oranges -> Oranges
Oranges m `mixOranges` Oranges n = Oranges (m + n)

main :: IO ()
main = do
    print $ Apples 4  `mixApples`  Apples 5
    print $ Oranges 4 `mixOranges` Oranges 5
    -- print $ Apples 4  `mixApples`  Oranges 5

Template Haskell

module Fruit2THHelper (genFruit) where

import Language.Haskell.TH

genFruit :: String -> Q [Dec]
genFruit fruit_name' = do
    let fun_name  = mkName ("mix" ++ fruit_name')
        fruit_name = mkName fruit_name'
    m <- newName "m"
    n <- newName "n"
    return
        [ NewtypeD [] fruit_name []
            (NormalC fruit_name [(NotStrict, ConT (mkName "Integer"))])
            [mkName "Show"]
        , SigD fun_name (ArrowT `AppT` ConT fruit_name
            `AppT` (ArrowT `AppT` ConT fruit_name `AppT` ConT fruit_name))
        , FunD fun_name
            [ Clause
                [ConP fruit_name [VarP m],ConP fruit_name [VarP n]]
                (NormalB (ConE fruit_name
                    `AppE` UInfixE (VarE m) (VarE (mkName "+")) (VarE n)))
                []
            ]
        ]
{-# LANGUAGE TemplateHaskell #-}
module Fruit2TH where

import Fruit2THHelper

genFruit "Apples"
genFruit "Oranges"

Type Classes

module Fruit3 where

newtype Apples  = Apples  Integer
newtype Oranges = Oranges Integer

class Fruit a where
    mix :: a -> a -> a

instance Fruit Apples where
    Apples m `mix` Apples n = Apples (m + n)

instance Fruit Oranges where
    Oranges m `mix` Oranges n = Oranges (m + n)

Template Haskell (Again)

{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Fruit3THHelper where

import Data.String
import Language.Haskell.TH

class Fruit a where
    mix :: a -> a -> a

instance IsString Name where
    fromString = mkName

genFruit :: String -> Q [Dec]
genFruit fruit_name' = do
    let fruit_name = mkName fruit_name'
    m <- newName "m"
    n <- newName "n"
    return
        [ NewtypeD [] fruit_name []
            (NormalC fruit_name [(NotStrict, ConT "Integer")])
            ["Show"]
        , InstanceD [] (ConT "Fruit" `AppT` ConT fruit_name)
            [ FunD "mix"
                [ Clause
                    [ConP fruit_name [VarP m],ConP fruit_name [VarP n]]
                    (NormalB (ConE fruit_name
                        `AppE` UInfixE (VarE m) (VarE "+") (VarE n)))
                    []
                ]
            ]
        ]
{-# LANGUAGE TemplateHaskell #-}
module Fruit3TH where

import Fruit3THHelper

genFruit "Apples"
genFruit "Oranges"

Phantom Types

module FruitPhantom where

data Count a = Count Integer deriving Show

mix :: Count a -> Count a -> Count a
Count m `mix` Count n = Count (m + n)

data Apples
data Oranges

Phantom Types in the Wild

GADTs

{-# LANGUAGE GADTs #-}
{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}
module FruitGADTs where

data Apples
data Oranges

data Fruit a where
    Apples  :: Integer -> Fruit Apples
    Oranges :: Integer -> Fruit Oranges

mix :: Fruit a -> Fruit a -> Fruit a
Apples m  `mix` Apples n  = Apples (m + n)
Oranges m `mix` Oranges n = Oranges (m + n)

(Generalized Abstract Data Types)

Template Haskell (Again Again)

Notes

Questions?

Attributions