Target - Haskell¶
Quick Start¶
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 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Record Type¶
Record type are represented Haskell record
with deriving clause specified in command line options.
data Book
= Book
{ id :: Int32
, name :: String
}
becomes
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.
data Color
= Red
| Green
| Blue
becomes
data Color
= Red
| Green
| Blue
deriving (Eq, Show)
Foreign Type¶
data DateTime
{-# FOREIGN haskell Data.Time.LocalTime.LocalTime #-}
becomes
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.
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
cis the value used in encoding and decoding. Usually you can use a type simplydata CodecImpl = CodecImpl.The
ris the serialized type encoding to or decoding from. e.g.ByteString.The
gis the functor applied to the final result. Usually has an instance ofMonadErrorin case something goes wrong.The
fis the functor applied to the intermediate result. It must have anApplicativeinstance, and usually have instances ofMonadError/MonadState.
You might find
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.
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
\(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
execStateTTransform the state returned to serialized type,
r
For a decodeValue, you usually should
Transform the
rinto an initial stateFeed it to
runStateTMake sure the returned state did not go wrong
Return the decoded value returned by
runStateT
Encode/Decode Record Type¶
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¶
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¶
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.
module Hello where
foo :: FooReq -> IO FooRes
bar :: BarReq -> IO BarRes
becomes
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 withIO.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¶
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 decoderb -> ha rb, the response encoderf 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 examples/lang/haskell/app/jsonhttp-server.hs for how to use the it.
Client Usage¶
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 encoderrb -> hb b, the response decoderQualName Text, the function namef a, the request
and should return m (g b), the response.
Please consult
examples/lang/haskell/app/jsonhttp-client.hs
for how to write a ClientTransport.
Command Line Options¶
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