packages feed

HList-0.5.4.0: examples/HListExample/MainGhcGeneric1.hs

{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}

{-

   (C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke

   This is a main module for exercising a model with generic type
   cast and generic type equality.

-}

module HListExample.MainGhcGeneric1 (mainGhcGeneric1) where

import HListExample.Datatypes2
import Data.HList.CommonMain
import Control.Lens

import Properties.Common
import Test.Hspec

import Control.Monad.Writer

-- --------------------------------------------------------------------------

type Animal =  '[Key,Name,Breed,Price]

angus :: HList Animal
angus =  HCons (Key 42)
           (HCons (Name "Angus")
           (HCons  Cow
           (HCons (Price 75.5)
            HNil)))

tlist1 = hFoldr (HSeq HPrint) (return () :: IO ()) angus

testBasic = do
  it "basic" $ do
    -- tlist1 does IO. The equivalent using Writer
    let f = HSeq $ HComp (tell . (:[])) HShow
        f2 = Fun (tell . (:[]) . show) :: Fun Show (Writer [String] ())

        angusStr = ["Key 42",  "Name \"Angus\"", "Cow", "Price 75.5" ]

    execWriter (hFoldr f (return ()) angus) `shouldBe`
        angusStr

    execWriter (hFoldr (HSeq f2) (return ()) angus) `shouldBe`
        angusStr


    hAppend angus angus `shouldShowTo` 
      "H[Key 42,Name \"Angus\",Cow,Price 75.5,Key 42,Name \"Angus\",Cow,Price 75.5]"


testHArray = do
  it "HArray" $ do
    hProjectByHNats (hNats (HCons hZero (HCons hZero HNil))) angus `shouldShowTo`
      "H[Key 42]"

      -- Before:
      -- H[Key 42, Key 42]
      -- XXX I don't duplicate at present!
    hProjectByHNats (hNats (HCons hZero (HCons (hSucc hZero) HNil))) angus `shouldShowTo`
      "H[Key 42,Name \"Angus\"]"

    hProjectByHNats (undefined::Proxy ['HZero, 'HSucc 'HZero]) angus `shouldShowTo`
      "H[Key 42,Name \"Angus\"]"

    hProjectAwayByHNats (hNats (HCons hZero HNil)) angus `shouldShowTo`
      "H[Name \"Angus\",Cow,Price 75.5]"

    hSplitByHNats 
	    (undefined::Proxy ['HZero, 'HSucc 'HZero])
	    angus
      `shouldShowTo`
      "(H[Key 42,Name \"Angus\"],H[Cow,Price 75.5])"


  it "HOccurs" $ do
    (hOccurs angus :: Breed) `shouldShowTo` "Cow"
    hOccurs angus `shouldBe` Cow

    hOccurs (hBuild 1 ^. from tipHList) `shouldShowTo` "1"

    (null $ hOccurs $ hBuild [] ^. from tipHList) `shouldBe` True
    (hProject angus :: HList '[Key, Name]) `shouldShowTo`
      "H[Key 42,Name \"Angus\"]"


  it "TypeIndexed" $ do
    let typeIdx1 = hDeleteMany (undefined::Proxy Name) angus
        typeIdx2 = BSE .*. angus
    typeIdx1 `shouldShowTo` "H[Key 42,Cow,Price 75.5]"
    typeIdx2 `shouldShowTo` "H[BSE,Key 42,Name \"Angus\",Cow,Price 75.5]"

    hUpdateAt Sheep typeIdx1 `shouldShowTo`
      "H[Key 42,Sheep,Price 75.5]"
  
    hDeleteAt (undefined::Proxy Breed) typeIdx2 `shouldShowTo`
      "H[BSE,Key 42,Name \"Angus\",Price 75.5]"

    hProjectBy (undefined::Proxy '[Breed]) angus `shouldShowTo` "H[Cow]"

    hProject angus `shouldBe` HCons Cow HNil

    -- doesn't work
    -- hProjectBy Proxy angus `shouldBe` HCons Cow HNil

    hSplitBy (undefined:: Proxy '[Breed]) angus `shouldShowTo`
      "(H[Cow],H[Key 42,Name \"Angus\",Price 75.5])"

testTIP = do
  -- |
  -- This example from the TIR paper challenges singleton lists.
  -- Thanks to the HW 2004 reviewer who pointed out the value of this example.
  -- We note that the explicit type below is richer than the inferred type.
  -- This richer type was needed for making this operation more polymorphic.
  -- That is, /a)/ would not work without the explicit type, 
  -- while /b/ would:
  --
  -- >  a)  ((+) (1::Int)) $ snd $ tuple oneTrue
  -- >  b)  ((+) (1::Int)) $ fst $ tuple oneTrue
  --
  -- As of 2014, type signatures are not needed to define tipyTuple.
  it "tipyTuple" $ do
    let tuple l = tipyTuple l

        -- oneTrue :: TIP (TagR [Int, Bool])		-- inferred
        -- oneTrue :: TIP '[Tagged Int Int, Tagged Bool Bool] -- expanded out
        oneTrue = (1::Int) .*. True .*. emptyTIP

    case tuple oneTrue of
      (a,b) -> (a+(1::Int), not b) `shouldShowTo` "(2,False)"

    not (fst (tuple oneTrue)) `shouldShowTo` "False"
    tuple oneTrue `shouldBe` (1::Int,True)

    (((+) (1::Int)) $ fst $ tuple oneTrue) `shouldBe` 2
    (((+) (1::Int)) $ snd $ tuple oneTrue) `shouldBe` 2


  it "tip" $ do
    hOccurs myTipyCow `shouldBe` Cow
    (BSE .*. myTipyCow) `shouldShowTo` "TIPH[BSE,Key 42,Name \"Angus\",Cow,Price 75.5]"
    -- (Sheep .*. myTipyCow) `shouldBe`  _
    {- if we uncomment the line above, we get the type error
       about the violation of the TIP condition: Breed type
       occurs twice.

      No instance for (Fail * (TypeFound Breed))
    -}

    (Sheep .*. hDeleteAtLabel (Label :: Label Breed) myTipyCow)
        `shouldShowTo` "TIPH[Sheep,Key 42,Name \"Angus\",Price 75.5]"

    (Sheep .*. (myTipyCow .-. (Label :: Label Breed)))
        `shouldShowTo` "TIPH[Sheep,Key 42,Name \"Angus\",Price 75.5]"

    tipyUpdate Sheep myTipyCow
        `shouldShowTo` "TIPH[Key 42,Name \"Angus\",Sheep,Price 75.5]"




myTipyCow = tipHList # angus -- lens #

animalKey :: ( SubType l (TIP Animal) -- explicit
             , HOccurs Key l          -- implicit
             ) => l -> Key
animalKey = hOccurs

animalish :: SubType l (TIP Animal) => l -> l
animalish = id
animalKey' l = hOccurs (animalish l) :: Key


makeLabels3 "MyNS" (words "key name breed price")
{- ^ makeLabels3 generates something like
data MyNS = MyNS -- a name space for record labels

key   = firstLabel MyNS  (undefined::DKey)
name  = nextLabel  key   (undefined::DName)
breed = nextLabel  name  (undefined::DBreed)
price = nextLabel  breed (undefined::DPrice)

data DKey;   instance Show DKey   where show _ = "key"
data DName;  instance Show DName  where show _ = "name"
data DBreed; instance Show DBreed where show _ = "breed"
data DPrice; instance Show DPrice where show _ = "price"

-}

unpricedAngus =  key    .=. (42::Integer)
             .*. name   .=. "Angus"
             .*. breed  .=. Cow
             .*. emptyRecord


testRecords = describe "testRecords" $ it "tests" $ do

  unpricedAngus `shouldShowTo` "Record{key=42,name=\"Angus\",breed=Cow}"
  unpricedAngus .!. breed `shouldShowTo` "Cow"

  let test3 = hDeleteAtLabel breed unpricedAngus

  test3
    `shouldShowTo` "Record{key=42,name=\"Angus\"}"

  (breed .=. Sheep .@. unpricedAngus)
    `shouldShowTo` "Record{key=42,name=\"Angus\",breed=Sheep}"

  let test4 = price .=. 8.8 .*. unpricedAngus

  test4
    `shouldShowTo` "Record{price=8.8,key=42,name=\"Angus\",breed=Cow}"

  hProjectByLabels (labelsOf (breed `HCons` price `HCons` HNil)) test4
    `shouldShowTo` "Record{price=8.8,breed=Cow}"

  -- XXX extra Label shouldn't be needed?
  -- alternatively it could be a compile-time error...
  -- hProjectByLabels (hEndP $ hBuild breed price) test4
  --  `shouldShowTo` "Record{price=8.8,breed=Cow}"

  -- test7 should be the same as test4 but
  -- with the different order of labels
  (newLVPair breed Sheep) .*. test3
    `shouldShowTo` "Record{breed=Sheep,key=42,name=\"Angus\"}"


type AnimalCol = TagR [Key,Name,Breed,Price]


testTIC = describe "TIC" $ do
  it "show" $
    (myCol :: TIC AnimalCol) `shouldShowTo` "TIC{breed=Cow}"
  it "hOccurs found" $
    (hOccurs myCol :: Maybe Breed) `shouldBe` Just Cow
  it "hOccurs absent" $
    (hOccurs myCol :: Maybe Price) `shouldBe` Nothing

myCol = mkTIC Cow :: TIC AnimalCol
{-

*TIC> mkTIC "42" :: TIC AnimalCol
Type error ...

*TIC> hOccurs myCol :: Maybe String
Type error ...

-- both of the these type errors could be better
-- (on ghc-7.10.3), Any is used to satisfy FD coverage condition, but the
-- TypeError context should be printed instead
<interactive>:170:1:
    Couldn't match type ‘Data.HList.CommonMain.Any’ with ‘[Char]’
    In the expression: mkTIC "42" :: TIC AnimalCol
    In an equation for ‘it’: it = mkTIC "42" :: TIC AnimalCol
-}

testVariant = describe "Variant" $ it "test" $ do
    testVar1 `shouldShowTo` "V{name=\"angus\"}"
    (testVar1 .!. key) `shouldBe` Nothing
    (testVar1 .!. name) `shouldBe` Just "angus"
 where
  testVar1 = mkVariant name "angus" animalVar

animalVar = asProxy $
               key   .=. (undefined :: Integer)
           .*. name  .=. (undefined :: String)
           .*. breed .=. (undefined :: Breed)
           .*. emptyRecord


asProxy :: proxy a -> Proxy a
asProxy _ = Proxy

mainGhcGeneric1 = describe "mainGhcGeneric1" $ do
  testBasic
  testHArray
  testTIP
  testRecords
  testTIC
  testVariant