content: {-# LANGUAGE DeriveGeneric #-}
  {-# LANGUAGE DuplicateRecordFields #-}
  {-# LANGUAGE UndecidableInstances #-}
  {-# LANGUAGE TypeApplications #-}
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE FlexibleContexts #-}
  
  -- Note: this file may contain spoilers
  -- (although I would be really surprised if it did, I haven't seen the films)
  module StarWars where
  
  import GHC.Generics
  import Data.Generics.Product
  
  data Episode = NEWHOPE | EMPIRE | JEDI
    deriving (Generic, Show, Eq)
  
  data Character = Character
     { name      :: String
     , friends   :: [Character]
     , appearsIn :: [Episode]
     } deriving (Generic, Show, Eq)
  
  data Human = Human
    { name        :: String
    , friends     :: [Character]
    , appearsIn   :: [Episode]
    , homePlanet  :: String
    } deriving (Generic, Show)
  
  data Droid = Droid
    { friends         :: [Character]
    , appearsIn       :: [Episode]
    , name            :: String
    , primaryFunction :: String
    } deriving (Generic, Show)
  
  luke :: Human
  luke = Human
    { name           = "Luke Skywalker"
    , friends        = []
    , appearsIn      = [NEWHOPE, EMPIRE, JEDI]
    , homePlanet     = "Saturn (?)"
    }
  
  r2d2 :: Droid
  r2d2 = Droid
    { name            = "R2-D2"
    , friends         = [upcast luke]
    , appearsIn       = [NEWHOPE, EMPIRE, JEDI]
    , primaryFunction = "repair ships"
    }
  
  c3po :: Droid
  c3po = Droid
    { name            = "C3PO"
    , friends         = [upcast r2d2, upcast luke]
    , appearsIn       = [NEWHOPE, EMPIRE, JEDI]
    , primaryFunction = "protocol and human relations"
    }
  
  getName :: HasField' "name" r a => r -> a
  getName = getField @"name"
  
  -- upcast :: Subtype a b => a -> b
  characters :: [Character]
  characters = [upcast r2d2, upcast luke, upcast c3po]
  
  names :: [String]
  names = map getName characters
  -- => ["R2-D2","Luke Skywalker","C3PO"]
  