mirror of
https://github.com/zap-proto/haskell.git
synced 2026-10-30 15:05:00 +00:00
83 lines
3.3 KiB
Haskell
83 lines
3.3 KiB
Haskell
{-# LANGUAGE DataKinds #-}
|
|
{-# LANGUAGE OverloadedLabels #-}
|
|
{-# LANGUAGE TypeApplications #-}
|
|
|
|
module Examples.Serialization.LowLevel.Write (main) where
|
|
|
|
import Zap.Gen.Addressbook
|
|
import qualified Zap as C
|
|
import Data.Function ((&))
|
|
import qualified Data.Text as T
|
|
|
|
main :: IO ()
|
|
main =
|
|
let Right msg = C.createPure C.defaultLimit buildMsg
|
|
in C.putMsg msg
|
|
|
|
buildMsg :: C.PureBuilder s (C.Message ('C.Mut s))
|
|
buildMsg = do
|
|
-- newMessage allocates a new, initially empty, mutable message. It
|
|
-- takes an optional size hint:
|
|
msg <- C.newMessage Nothing
|
|
|
|
-- newRoot allocates a new struct as the root object of the message.
|
|
-- The unit argument is a hint to the allocator to determine the size
|
|
-- of the object; for types whose size is not fixed (e.g. untyped structs,
|
|
-- lists), this may be something more meaningful.
|
|
addressbook <- C.newRoot @AddressBook () msg
|
|
|
|
-- newField can be used to allocate the value of a field, for pointer
|
|
-- types like lists. The number is the allocation hint, as used by newRoot.
|
|
-- We can use the OverloadedLabels extension to pass in fields by name.
|
|
people <- C.newField #people 2 addressbook
|
|
|
|
-- Index gets an object at a specified location in a list. Zap
|
|
-- lists are flat arrays, and in the case of structs the structs are
|
|
-- unboxed, so there is no need to allocate each element:
|
|
alice <- C.index 0 people
|
|
|
|
-- encodeField takes the parsed form of a value and marshals it into
|
|
-- the specified field. For basic types like integers & booleans, this
|
|
-- is almost always what you want. For larger values, you may want to
|
|
-- use newField as above, or separately create the value and use setField,
|
|
-- as shown below.
|
|
C.encodeField #id 123 alice
|
|
C.encodeField #name (T.pack "Alice") alice
|
|
C.encodeField #email (T.pack "[email protected]") alice
|
|
|
|
-- We would probably use newField here, but to demonstrate, we can allocate
|
|
-- the value separately with new, and then set it with setField.
|
|
phones <- C.new @(C.List Person'PhoneNumber) 1 msg
|
|
C.setField #phones phones alice
|
|
|
|
mobilePhone <- C.index 0 phones
|
|
-- It is sometimes more ergonomic to use (&) from Data.Function. You might
|
|
-- ask why not just make the container the first argument, but it works
|
|
-- out better this way for the read examples.
|
|
mobilePhone & C.encodeField #number (T.pack "555-1212")
|
|
mobilePhone & C.encodeField #type_ Person'PhoneNumber'Type'mobile
|
|
|
|
-- Since named unions act like unnamed unions inside a group, we first have
|
|
-- to get the group field:
|
|
employment <- C.readField #employment alice
|
|
|
|
-- Then, we can use encodeVariant to set both the tag of the union and the
|
|
-- value:
|
|
employment & C.encodeVariant #school (T.pack "MIT")
|
|
|
|
bob <- C.index 1 people
|
|
bob & C.encodeField #id 456
|
|
bob & C.encodeField #name (T.pack "Bob")
|
|
bob & C.encodeField #email (T.pack "[email protected]")
|
|
|
|
phones <- bob & C.newField #phones 2
|
|
homePhone <- phones & C.index 0
|
|
homePhone & C.encodeField #number (T.pack "555-4567")
|
|
homePhone & C.encodeField #type_ Person'PhoneNumber'Type'home
|
|
workPhone <- phones & C.index 1
|
|
workPhone & C.encodeField #number (T.pack "555-7654")
|
|
workPhone & C.encodeField #type_ Person'PhoneNumber'Type'work
|
|
employment <- bob & C.readField #employment
|
|
employment & C.encodeVariant #selfEmployed () -- Note the (), since selfEmploy is Void.
|
|
pure msg
|