packages feed

wakame-0.1.0.0: test/examples/Wakame/Examples/Functions.hs

{-# LANGUAGE DuplicateRecordFields #-}
module Wakame.Examples.Functions where

import Prelude

import GHC.Generics
import Wakame


-- * Data types which is used in this module

data Point = Point { x :: Double, y :: Double }
  deriving (Eq, Show, Generic)

data Position = Position { x :: !Double, y :: !Double }
  deriving (Eq, Show, Generic)

data Point3d = Point3d { x :: Double, y :: Double, z :: Double }
  deriving (Eq, Show, Generic)

data Tniop = Tniop { y :: Double, x :: Double }
  deriving (Eq, Show, Generic)


-- | An instance of @Point@ data type.
pt :: Point
pt = Point 1.2 8.3

-- | Review of @Generic@
-- Let's review the basic functionality of an instance of @Generic@ typeclass.
--
-- `from` function converts a data to its generic representation.
-- >>> from pt
-- M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = 1.2}} :*: M1 {unM1 = K1 {unK1 = 8.3}}}}
--
-- You can construct a representation value by hand.
-- >>> rep = M1 $ M1 $ M1 (K1 1.2) :*: M1 (K1 8.3)
--
-- From the representation value, `to` function converts back to @Point@.
-- >>> to rep :: Point
-- Point {x = 1.2, y = 8.3}
--
-- Since @Position@ has the same representation, `to` works as well.
-- >>> to rep :: Position
-- Position {x = 1.2, y = 8.3}
--
-- And even so to Tuple.
-- >>> to rep :: (Double, Double)
-- (1.2,8.3)
--
-- It is sensitive to the order of elements.
-- >>> to rep :: Tniop
-- Tniop {y = 1.2, x = 8.3}



-- * Examples of record value manipulation

-- | Round trip of Point to/from Row
-- >>> fromRow @Point $ toRow pt
-- Point {x = 1.2, y = 8.3}

-- | Converting Point to Point3d by adding z field
-- >>> fromRow @Point3d $ union (toRow pt) (toRow (keyed @"z" 42.0))
-- Point3d {x = 1.2, y = 8.3, z = 42.0}


-- | Interfacing optional fields
--
-- `f` fills absent fileds with `0.0`.
-- >>> f $ toRow (keyed @"x" 3.5)
-- Point {x = 3.5, y = 0.0}
--
-- Ignores extra fields.
-- >>> f $ toRow (keyed @"y" 4.3, keyed @"z" 1.6)
-- Point {x = 0.0, y = 4.3}
--
-- Converts from another type of data.
-- >>> f $ toRow $ Point3d 3.5 4.3 1.6
-- Point {x = 3.5, y = 4.3}
--
-- Returns fully default value when `()` is given.
-- >>> f $ toRow ()
-- Point {x = 0.0, y = 0.0}
--
-- Works nicely no matter how the order of fields is.
-- >>> f $ toRow $ Tniop 4.3 3.5
-- Point {x = 3.5, y = 4.3}
f ::
  ( Merge props OfPoint OfPoint
  ) => Row props -> Point
f props = fromRow $ merge props def
  where
    def :: Row OfPoint
    def = toRow $ Point 0.0 0.0

type OfPoint = Of Point


-- | Filling common fields if existing
--
-- >>> g (Person "Luke" "1979-01-01") (Timestamp "2020-04-01" "2020-04-02") :: Person
-- Person {name = "Luke", created_at = "2020-04-01"}
--
-- >>> g (Point 3.5 4.3) (Timestamp "2020-04-01" "2020-04-02") :: Point
-- Point {x = 3.5, y = 4.3}

data Timestamp =
  Timestamp
  { created_at :: String
  , updated_at :: String
  }
  deriving (Eq, Show, Generic)

data Person =
  Person
  { name       :: String
  , created_at :: String
  }
  deriving (Eq, Show, Generic)

g ::
  ( IsRow a
  , IsRow b
  , IsRow c
  , Merge (Of b) (Of a) (Of c)
  ) => a -> b -> c
g x y = fromRow $ nub $ union (toRow y) (toRow x)


-- | Rejecting certain fields
--
-- `h` is the same as `g` except rejecting the first argument of having a field "y".
-- >>> h (Point 3.5 4.3) (Timestamp "2020-04-01" "2020-04-02") :: Point
-- ...
-- ... Couldn't match type ...
-- ...

h ::
  ( IsRow a
  , IsRow b
  , IsRow c
  , Merge (Of b) (Of a) (Of c)
  , Lacks "y" (Of a)
  ) => a -> b -> c
h x y = fromRow $ nub $ union (toRow y) (toRow x)