packages feed

pec-0.2.0: test_cases/TestVariant.pec

module TestVariant

imports
  Prelude

where

main :: () -> I32
main () = do
  testEnum ()
  testNewtype ()
  testNewtypePoly ()
  testUnit ()
  testVariant ()
  0

type Color = | Red | Blue | Green // the standard enum example, order doesn't matter

type PHColor x = | PHRed | PHBlue // phantom type enum

instance Eq Color // eq and ne can be derived for enums
instance Eq (PHColor x)

// enums can't be automatically cast to ints, that leads to maintenance problems
unColor :: Color -> W32
unColor c = switch c of
  Red -> 0
  Blue -> 1
  Green -> 2 // All tags must be matched.  Removing this line will cause a compile time error.

sColor :: Color -> IString
sColor c = switch c of // switch on enums is by value
  Red -> "Red"
  Blue -> "Blue"
  Green -> "Green"

type Unit = | Unit // singleton type, isomorphic to ()

unitf :: () -> Unit
unitf () = Unit

unita :: Unit -> ()
unita _ = () // todo(?): unita Unit = ()

testUnit :: () -> ()
testUnit () = do
  putLn "testunit"
  x = Unit // singleton types are compiled into "void" in LLVM/C
  unita x
  _ = unitf ()
  unita Unit
  putLn "done"

testEnum :: () -> ()
testEnum () = do
  putLn "testEnum"
  assert (Red == Red)
  x = Green // enum types are compiled into unsigned ints in LLVM/C
  putLn (sColor x)
  assert (Green == x)
  assert (x == Green)
  assert (x != Blue)
  p = new Blue
  assert (@p == Blue)
  putLn (sColor @p)
  p <- Red
  assert (@p != Blue)
  putLn (sColor @p)
  p <- x
  putLn (sColor @p)
  assert (@p == Green)
  assert (unColor @p == 2)
  assert (unColor Blue == 1)
  switch Red of
    Green -> assert False
    Red -> assert True
    color -> assert (color != Blue)
  switch x of
    Green -> assert True
    Red -> assert False
    Blue -> assert False
  pc = (PHRed :: PHColor Bool)
  assert (pc == PHRed)
//  assert (pc == (PHRed :: PHColor ())) // type error
  putLn "done"

type Meters = | M W32 // newtype.  A variant with only one tag

instance Eq Meters

testNewtype :: () -> ()
testNewtype () = do
  putLn "testNewtype"
  assert (M 7 == M 7)
// newtypes are constructed by juxtaposition like in Haskell
// newtypes are compiled into whatever the underlying type is in LLVM/C (i.e. the tag is eliminated)
  x = M 12
  assert (x == M 12)
  p = new (M 7)
  assert (@p != M 12)
  assert (@p == M 7)
  p <- M 47
  assert (@p == M 47)
  assert (47 == m_unwrap @p) // instead of using "case" to extract values from newtypes, a function is created which will unwrap the type, it has the form <constr>_unwrap.  Also <constr>_unwrapptr is created which will safely cast a newtype ptr to a ptr.
  p <- M 1
  i = m_unwrap @p
  assert (i == 1)
  putLn "done"

type MyA a = | My a // polymorphic newtype

testNewtypePoly :: () -> ()
testNewtypePoly () = do
  putLn "testNewtypePoly"
  x = My 'c'
  p = new x
  assert (my_unwrap @p == 'c')
  q = new (My True)
  assert (my_unwrap @q)
  putLn "done"

testVariant :: () -> ()
testVariant () = do
  putLn "testVariant"
  p = new (Left False)
  p <- Right 'c' // p :: Ptr (Either Bool Char)
  // variants must be accessed via a pointer
  case p of
    Left a -> do
      assert @a
    Right b -> do // b :: Ptr Char, not Char
      putCh @b
      assert (@b == 'c')
  p <- Left True // store the value "Left True" in p
  case p of
    Left a -> assert @a
    Right b -> do
      putCh @b
      assert False
  q = new (Nothing :: Maybe Bool)
  case q of
    Nothing -> assert True
    Just _ -> assert False
  q <- Just True
  case q of
    Just b -> assert @b
    _ -> assert False
  r = new (Left True)
  case r of
    Left b -> assert @b
    Right _ -> assert False
  r <- Right (27 :: W32)
  case r of
    Left _ -> assert False
    Right i -> do
      putW @i
      assert True
  putLn "done"