Target - Haskell ================ Quick Start ----------- .. sourcecode:: bash gugugu-haskell \ --input=dir/containing/gugugu/definitions \ --output=dir/containing/haskell/code \ --module-prefix=Some.Prefix \ --derivings=Eq,Show \ --with-codec \ --with-server \ --with-client \ ; Module ------ Gugugu module is represented by Haskell module, with module name unchanged. Types ----- Primitives ~~~~~~~~~~ +-------------+--------------------------+ | Gugugu Type | Haskell Type | +=============+==========================+ | ``Unit`` | ``()`` | +-------------+--------------------------+ | ``Bool`` | ``Bool`` | +-------------+--------------------------+ | ``Int32`` | ``Data.Int.Int32`` | +-------------+--------------------------+ | ``Double`` | ``Double`` | +-------------+--------------------------+ | ``String`` | ``Data.Text.Text`` | +-------------+--------------------------+ | ``Maybe A`` | ``Maybe A`` | +-------------+--------------------------+ | ``List A`` | ``Data.Vector.Vector A`` | +-------------+--------------------------+ Record Type ~~~~~~~~~~~ Record type are represented Haskell record with ``deriving`` clause specified in command line options. .. sourcecode:: haskell data Book = Book { id :: Int32 , name :: String } becomes .. sourcecode:: haskell data Book = Book { bookId :: Int32 , bookName :: Text } deriving (Eq, Show) Enum Type ~~~~~~~~~ Enum type are represented by Haskell sum type with ``deriving`` clause specified in command line options. .. sourcecode:: haskell data Color = Red | Green | Blue becomes .. sourcecode:: haskell data Color = Red | Green | Blue deriving (Eq, Show) Foreign Type ~~~~~~~~~~~~ .. sourcecode:: haskell data DateTime {-# FOREIGN haskell Data.Time.LocalTime.LocalTime #-} becomes .. sourcecode:: haskell type DateTime = Data.Time.LocalTime.LocalTime Encoder and Decoder ------------------- All types in this section are located in module ``Gugugu.Lang.Haskell.Runtime.Codec`` with default configuration. .. sourcecode:: haskell class Encoding a where encode :: (EncoderImpl c r g f, Applicative f) => c -> a -> f () class Decoding a where decode :: (DecoderImpl c r g f, Applicative f) => c -> f a All types generated by gugugu will have instances for class ``Encoding`` and ``Decoding``. The ``c`` is a value you have to provide to describe how to encode and decode a value. You also have to provide the ``EncoderImpl`` or ``DecoderImpl`` instance. EncoderImpl and DecoderImpl ~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are two typeclasses named ``EncoderImpl`` and ``DecoderImpl`` which you should provide implementation to do the encoding/decoding. They have kind ``* -> * -> (* -> *) -> (* -> *) -> Constraint``. - The ``c`` is the value used in encoding and decoding. Usually you can use a type simply ``data CodecImpl = CodecImpl``. - The ``r`` is the serialized type encoding to or decoding from. e.g. ``ByteString``. - The ``g`` is the functor applied to the final result. Usually has an instance of ``MonadError`` in case something goes wrong. - The ``f`` is the functor applied to the intermediate result. It must have an ``Applicative`` instance, and usually have instances of ``MonadError`` / ``MonadState``. You might find :gugugu-source:`examples/lang/haskell/src/GuguguExamples/Codec/Json.hs` useful to write ``EncoderImpl``/``DecoderImpl``. Most class members do not use ``r`` and ``g`` except the following two. .. sourcecode:: haskell class ForeignEncodersImpl c f => EncoderImpl c r g f | c -> r g f where encodeValue :: Encoding a => c -> a -> g r -- example encodeValue c a = runEncoding $ encode c a where runEncoding :: f () -> g r runEncoding = undefined class ForeignDecodersImpl c f => DecoderImpl c r g f | c -> r g f where decodeValue :: Decoding a => c -> r -> g a -- example decodeValue c r = runDecoding r $ decode c where runDecoding :: r -> f a -> g a runDecoding = undefined For most implementation, which is used in the examples, there exists a ``t``, which has an instance of ``MonadTrans``, and :math:`f \cong t (g)`, If you are using the ``StateT SomeState g`` as the ``f``. For an ``encodeValue``, you usually should * Provide an initial state * Feed it to ``execStateT`` * Transform the state returned to serialized type, ``r`` For a ``decodeValue``, you usually should * Transform the ``r`` into an initial state * Feed it to ``runStateT`` * Make sure the returned state did not go wrong * Return the decoded value returned by ``runStateT`` Encode/Decode Record Type ~~~~~~~~~~~~~~~~~~~~~~~~~ .. sourcecode:: haskell class ForeignEncodersImpl c f => EncoderImpl c r g f | c -> r g f where encodeRecord :: Int -> (c -> a -> f ()) -> c -> a -> f () encodeRecordField :: Encoding a => Int -> Text -> c -> a -> f () class ForeignDecodersImpl c f => DecoderImpl c r g f | c -> r g f where decodeRecord :: Int -> (c -> f a) -> c -> f a decodeRecordField :: Decoding a => Int -> Data -> c -> f a The generated encoder/decoder for record type consists of a call to ``encodeRecord``/``decodeRecord`` with the number of fields. And the provided callback will call the ``encodeRecordField``/``decodeRecordField`` several times with indices and names of the fields. Encode/Decode Enum Type ~~~~~~~~~~~~~~~~~~~~~~~ .. sourcecode:: haskell class ForeignEncodersImpl c f => EncoderImpl c r g f | c -> r g f where encodeEnum :: (a -> Int) -> (a -> Text) -> c -> a -> f () class ForeignDecodersImpl c f => DecoderImpl c r g f | c -> r g f where decodeEnum :: (Int -> Maybe a) -> (Text -> Maybe a) -> c -> f a The generated encoder/decoder for enum type consists of a call to ``encodeEnum``/``decodeEnum``. You should encode/decode the value with the name or the index. Encode/Decode Primitive and Foreign Types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. sourcecode:: haskell class ForeignEncodersImpl c f where -- foreign encoder members, e.g. -- encodeDateTime :: c -> LocalTime -> f () class ForeignDecodersImpl c f where -- foreign decoder members, e.g. -- decodeDateTime :: c -> f LocalTime class ForeignEncodersImpl c f => EncoderImpl c r g f | c -> r g f where encodeMaybe :: Encoding a => c -> Maybe a -> f () encodeList :: Encoding a => c -> Vector a -> f () encodeUnit :: c -> () -> f () encodeBool :: c -> Bool -> f () encodeInt32 :: c -> Int32 -> f () encodeDouble :: c -> Double -> f () encodeString :: c -> Text -> f () class ForeignDecodersImpl c f => DecoderImpl c r g f | c -> r g f where decodeMaybe :: Decoding a => c -> f (Maybe a) decodeList :: Decoding a => c -> f (Vector a) decodeUnit :: c -> f () decodeBool :: c -> f Bool decodeInt32 :: c -> f Int32 decodeDouble :: c -> f Double decodeString :: c -> f Text The primitive types and foreign types will generate functions like above. And the encoder/decoder simply calls the function you provide. Client and Server ----------------- All types in this section are located in module ``Gugugu.Lang.Haskell.Runtime.Transport`` with default configuration. .. sourcecode:: haskell module Hello where foo :: FooReq -> IO FooRes bar :: BarReq -> IO BarRes becomes .. sourcecode:: haskell class HelloModule a f g m where foo :: a -> f FooReq -> m (g FooRes) bar :: a -> f BarReq -> m (g BarRes) mkHelloTransport :: ( DecoderImpl ca ra ha fa , EncoderImpl cb rb hb fb , HelloModule a f g m ) => ca -> cb -> a -> ServerTransport f g m ra rb ha hb instance HelloModule (GuguguClient f g m ra rb ha hb) f g m where The ``a`` can be used as the client when used in client code, or as the server implementation in server code. The ``ra`` and ``ha`` is the serialized type and the functor used by request, The ``rb`` and ``hb`` is the serialized type and the functor used by response. They are usually the same type but not necessary. Some typical use of ``f``, ``g`` and ``m`` are list below. ``f`` can be - ``Identity``, when you just want to pass the value. - ``data WithMeta a = WithMeta SomeMeta a``, when you want some metadata with your request, such as authentication data. - ``[]``, when you want to process many data in one request. ``g`` can be - ``Identity``, when you just want to pass the value. - ``data WithMeta a = WithMeta SomeMeta a``, when you want to return some metadata to with your response, such as request ID, processed time, etc. - ``[]``, when you want to return many data in one request. - ``Either ErrorInfo``. when you want error handling. ``m`` can be - ``Identity``, when you have a pure implementation. - ``IO``, when you want to handle it with ``IO``. - ``ContT r IO``, if you want delimited continuation. .. warning:: Do *not* use any type that cannot be converted into ``data WithMeta a = WithMeta SomeMeta a`` (such as ``[a]``) as ``f`` or ``g`` if you want to work with other target that does not support polymorphism over higher-kinded types. Most targets do not support polymorphism over higher-kinded types. Server Usage ~~~~~~~~~~~~ .. sourcecode:: haskell data QualName a = QualName (Vector a) a deriving Show type ServerCodecHandler f g m ra rb ha hb = forall a b. (ra -> ha a ) -> ( b -> hb rb ) -> (f a -> m (g b)) -> f ra -> m (g rb) type ServerTransport f g m ra rb ha hb = QualName Text -> ServerCodecHandler f g m ra rb ha hb -> Maybe (f ra -> m (g rb)) mkHelloTransport :: ( DecoderImpl ca ra ha fa , EncoderImpl cb rb hb fb , HelloModule a f g m ) => ca -> cb -> a -> ServerTransport f g m ra rb ha hb ``mkHelloTransport`` converts an ``a`` into a ``ServerTransport f g m ra rb ha hb``. The ``ServerCodecHandler f g m ra rb ha hb`` is called with, - ``ra -> ha a``, the request decoder - ``b -> ha rb``, the response encoder - ``f a -> m (g b)``, the real handler you provided in the class instance and should return ``f ra -> m (g rb)``, the handler with ``ra`` and ``rb`` with decoding/encoding handled. Please consult :gugugu-source:`examples/lang/haskell/app/jsonhttp-server.hs` for how to use the it. Client Usage ~~~~~~~~~~~~ .. sourcecode:: haskell data QualName a = QualName (Vector a) a deriving Show data GuguguClient f g m ra rb ha hb = forall ca cb fa fb. ( EncoderImpl ca ra ha fa , DecoderImpl cb rb hb fb ) => MkGuguguClient ca cb (ClientTransport f g m ra rb ha hb) type ClientTransport f g m ra rb ha hb = forall a b. (a -> ha ra) -> (rb -> hb b) -> QualName Text -> f a -> m (g b) instance HelloModule (GuguguClient f g m ra rb ha hb) f g m where Like ``ServerTransport``, ``ClientTransport`` can handle request about type ``ra`` and return an response about type ``rb``. The ``ClientTransport`` is called with, - ``a -> ha ra``, the request encoder - ``rb -> hb b``, the response decoder - ``QualName Text``, the function name - ``f a``, the request and should return ``m (g b)``, the response. Please consult :gugugu-source:`examples/lang/haskell/app/jsonhttp-client.hs` for how to write a ``ClientTransport``. Command Line Options -------------------- .. sourcecode:: none Usage: gugugu-haskell (-i|--input INPUT) (-o|--output OUTPUT) (-p|--module-prefix MODULE_PREFIX) [-r|--runtime-module RUNTIME_MODULE] [--derivings DERIVINGS] [--with-codec] [--with-server] [--with-client] [--trans-module-code ARG] [--trans-module-value ARG] [--trans-module-type ARG] [--trans-func-code ARG] [--trans-func-value ARG] [--trans-type-code ARG] [--trans-type-func ARG] [--trans-field-code ARG] [--trans-field-value ARG] [--trans-enum-code ARG] [--trans-enum-value ARG] [--version] Available options: -i,--input INPUT the directory containing the definition files -o,--output OUTPUT the directory to put the generated sources -p,--module-prefix MODULE_PREFIX the package prefix, e.g. Some.Prefix -r,--runtime-module RUNTIME_MODULE location of gugugu runtime module (default: "Gugugu.Lang.Haskell.Runtime") --derivings DERIVINGS deriving clause for data type, use comma to separate multiples, e.g. Eq,Show --with-codec pass this flag to generate codecs, default to false --with-server pass this flag to generate server, default to false, implies with-codec --with-client pass this flag to generate client, default to false, implies with-codec --trans-module-code ARG module name transformer for code (default: id) --trans-module-value ARG module name transformer for value (default: snake) --trans-module-type ARG module name transformer for type of client/server (default: id) --trans-func-code ARG function name transformer for code (default: id) --trans-func-value ARG function name transformer for value (default: snake) --trans-type-code ARG type name transformer for code (default: id) --trans-type-func ARG type name transformer in function (default: id) --trans-field-code ARG record field name transformer for code (default: id) --trans-field-value ARG record field name transformer for value (default: snake) --trans-enum-code ARG enum name transformer for code (default: id) --trans-enum-value ARG enum name transformer for value (default: upper-snake) -h,--help Show this help text --help-transformers list available name transformers --version show version