Safe Haskell | None |
---|---|
Language | Haskell2010 |
The Shwifty library allows generation of
Swift types (structs and enums) from Haskell
ADTs, using Template Haskell. The main
entry point to the library should be the
documentation and examples of getShwifty
.
See also getShwiftyWith
and getShwiftyWithTags
.
This library is in alpha and there are a number of known bugs which shouldn't affect most users. See the issue tracker to see what those are.
There are probably many bugs/some weird behaviour when it comes to data families. Please report any issues on the issue tracker.
Synopsis
- class ToSwift a where
- class ToSwiftData a where
- toSwiftData :: Proxy a -> SwiftData
- prettySwiftData :: Proxy a -> String
- getShwifty :: Name -> Q [Dec]
- getShwiftyWith :: Options -> Name -> Q [Dec]
- getShwiftyWithTags :: Options -> [Name] -> Name -> Q [Dec]
- data Ty
- data SwiftData
- = SwiftStruct { }
- | SwiftEnum { }
- | TypeAlias { }
- data Protocol
- data Options
- $sel:fieldLabelModifier:Options :: Options -> String -> String
- $sel:constructorModifier:Options :: Options -> String -> String
- $sel:optionalExpand:Options :: Options -> Bool
- $sel:indent:Options :: Options -> Int
- $sel:generateToSwift:Options :: Options -> Bool
- $sel:generateToSwiftData:Options :: Options -> Bool
- $sel:dataProtocols:Options :: Options -> [Protocol]
- $sel:dataRawValue:Options :: Options -> Maybe Ty
- $sel:typeAlias:Options :: Options -> Bool
- $sel:newtypeTag:Options :: Options -> Bool
- $sel:lowerFirstCase:Options :: Options -> Bool
- $sel:lowerFirstField:Options :: Options -> Bool
- defaultOptions :: Options
- prettyTy :: Ty -> String
- prettySwiftData :: ToSwiftData a => Proxy a -> String
- type X = Void
Classes for conversion
class ToSwift a where Source #
The class for things which can be converted to
a Swift type (Ty
).
Typically the instance will be generated by
getShwifty
.
Instances
class ToSwiftData a where Source #
The class for things which can be converted to
SwiftData
.
Typically the instance will be generated by
getShwifty
.
Generating instances
getShwifty :: Name -> Q [Dec] Source #
Generate ToSwiftData
and ToSwift
instances
for your type. ToSwift
instances are typically
used to build cases or fields, whereas
ToSwiftData
instances are for building structs
and enums. Click the Examples
button to see
examples of what Swift gets generated in
different scenarios. To get access to the
generated code, you will have to use one of
the pretty-printing functions provided.
Examples
-- A simple sum type data SumType = Sum1 | Sum2 | Sum3 getShwifty ''SumType
enum SumType { case sum1 case sum2 case sum3 }
-- A simple product type data ProductType = ProductType { x :: Int, y :: Int } getShwifty ''ProductType
struct ProductType { let x: Int let y: Int }
-- A sum type with type variables data SumType a b = SumL a | SumR b getShwifty ''SumType
enum SumType<A, B> { case sumL(A) case sumR(B) }
-- A product type with type variables data ProductType a b = ProductType { aField :: a, bField :: b } getShwifty ''ProductType
struct ProductType<A, B> { let aField: A let bField: B }
-- A newtype newtype Newtype a = Newtype { getNewtype :: a } getShwifty ''Newtype
struct Newtype<A> { let getNewtype: A }
-- A type with a function field newtype Endo a = Endo { appEndo :: a -> a } getShwifty ''Endo
struct Endo<A> { let appEndo: ((A) -> A) }
-- A type with a kookier function field newtype Fun a = Fun { fun :: Int -> Char -> Bool -> String -> Maybe a } getShwifty ''Fun
struct FunA { let fun: ((Int, Char, Bool, String) -> A?) }
-- A weird type with nested fields. Also note the Result's types being flipped from that of the Either. data YouveGotProblems a b = YouveGotProblems { field1 :: Maybe (Maybe (Maybe a)), field2 :: Either (Maybe a) (Maybe b) } getShwifty ''YouveGotProblems
struct YouveGotProblems<A, B> { let field1: Option<Option<Option<A>>> let field2: Result<Option<B>,Option<A>> }
-- A type with polykinded type variables -- Also note that there is no newline because -- of the absence of fields data PolyKinded (a :: k) = PolyKinded getShwifty ''PolyKinded
struct PolyKinded<A> { }
-- A sum type where constructors might be records data SumType a b (c :: k) = Sum1 Int a (Maybe b) | Sum2 b | Sum3 { x :: Int, y :: Int } getShwifty ''SumType
enum SumType<A, B, C> { case field1(Int, A, Optional<B>) case field2(B) case field3(_ x: Int, _ y: Int) }
-- A type containing another type with instance generated by 'getShwifty' newtype MyFirstType a = MyFirstType { getMyFirstType :: a } getShwifty ''MyFirstType data Contains a = Contains { x :: MyFirstType Int, y :: MyFirstType a } getShwifty ''Contains
struct MyFirstType<A> { let getMyFirstType: A } struct Contains<A> { let x: MyFirstType<Int> let y: MyFirstType<A> }
getShwiftyWith :: Options -> Name -> Q [Dec] Source #
Like getShwifty
, but lets you supply
your own Options
. Click the examples
for some clarification of what you can do.
Examples
data PrefixedFields = MkPrefixedFields { prefixedFieldsX :: Int, prefixedFieldsY :: Int } $(getShwiftyWith (defaultOptions { fieldLabelModifier = drop (length "PrefixedFields") }) ''PrefixedFields)
struct PrefixedFields { let x: Int let y: Int }
data PrefixedCons = MkPrefixedConsLeft | MkPrefixedConsRight $(getShwiftyWith (defaultOptions { constructorModifier = drop (length "MkPrefixedCons"), dataProtocols = [Codable] }) ''PrefixedCons)
enum PrefixedCons: Codable { case left case right }
getShwiftyWithTags :: Options -> [Name] -> Name -> Q [Dec] Source #
Like getShwiftyWith
, but lets you supply
tags. Tags are type-safe typealiases that
are akin to newtypes in Haskell. The
introduction of a struct around something
which is, say, a UUID in Swift means that
the default Codable instance will not work
correctly. So we introduce a tag(s). See the
examples to see how this looks. Also, see
https://github.com/pointfreeco/swift-tagged,
the library which these tags use. The library
is not included in any generated code.
Examples
-- Example of using the swift-tagged library: -- A type containing a database key data User = User { id :: UserId, name :: Text } -- the user key newtype UserId = UserId UUID $(getShwiftyWithTags defaultOptions [ ''UserId ] ''User) -- A type that also contains the UserId data UserDetails = UserDetails { id :: UserId, lastName :: Text } getShwifty ''UserDetails
struct User { let id: UserId let name: String typealias UserId = Tagged<User,UUID> } struct UserDetails { let id: User.UserId let lastName: String }
-- Example type with multiple tags newtype Name = MkName String newtype Email = MkEmail String data Person = Person { name :: Name, email :: Email } $(getShwiftyWithTags defaultOptions [ ''Name, ''Email ] ''Person)
struct Person { let name: Name let email: Email enum NameTag {} typealias Name = Tagged<NameTag, String> enum EmailTag {} typealias Email = Tagged<EmailTag, String> }
Types
An AST representing a Swift type.
Unit | Unit (called "Unit/Void" in swift). Empty struct type. |
Bool | Bool |
Character | Character |
Str | |
I | signed machine integer |
I8 | signed 8-bit integer |
I16 | signed 16-bit integer |
I32 | signed 32-bit integer |
I64 | signed 64-bit integer |
U | unsigned machine integer |
U8 | unsigned 8-bit integer |
U16 | unsigned 16-bit integer |
U32 | unsigned 32-bit integer |
U64 | unsigned 64-bit integer |
F32 | 32-bit floating point |
F64 | 64-bit floating point |
Decimal | Increased-precision floating point |
BigSInt32 | 32-bit big integer |
BigSInt64 | 64-bit big integer |
Tuple2 Ty Ty | 2-tuple |
Tuple3 Ty Ty Ty | 3-tuple |
Optional Ty | Maybe type |
Result Ty Ty | Either type Note: The error type in Swift must
implement the |
Set Ty | Set type |
Dictionary Ty Ty | Dictionary type |
Array Ty | array type |
App Ty Ty | function type |
Poly String | polymorphic type variable |
Concrete | a concrete type variable, and its
type variables. Will typically be generated
by |
Tag | A See |
|
Instances
A Swift datatype, either a struct (product type) or enum (sum type). Haskll types are sums-of-products, so the way we differentiate when doing codegen, is that types with a single constructor will be converted to a struct, and those with two or more constructors will be converted to an enum. Types with 0 constructors will be converted to an empty enum.
Note: There seems to be a haddock bug related
to `-XDuplicateRecordFields` which
causes the haddocks for the fields of SwiftEnum
to refer to struct.
SwiftStruct | A struct (product type) |
SwiftEnum | An enum (sum type) |
| |
TypeAlias | A top-level type alias |
Instances
Swift protocols. Only a few are supported right now.
Hashable | The |
Codable | The |
Equatable | The |
Options for encoding types
Option type
Options that specify how to
encode your SwiftData
to a swift type.
Options can be set using record syntax on
defaultOptions
with the fields below.
Actual Options
$sel:fieldLabelModifier:Options :: Options -> String -> String Source #
Function applied to field labels.
Handy for removing common record prefixes,
for example. The default (id
) makes no
changes.
$sel:constructorModifier:Options :: Options -> String -> String Source #
Function applied to value constructor names.
The default (id
) makes no changes.
$sel:indent:Options :: Options -> Int Source #
Number of spaces to indent field names and cases. The default is 4.
$sel:generateToSwiftData:Options :: Options -> Bool Source #
Whether or not to generate a ToSwiftData
instance. Sometime this can be desirable
if you want to define the instance by hand,
or the instance exists elsewhere.
The default is True
, i.e., to generate
the instance.
$sel:dataProtocols:Options :: Options -> [Protocol] Source #
Protocols to add to a type.
The default ([]
) will add none.
$sel:dataRawValue:Options :: Options -> Maybe Ty Source #
The rawValue of an enum. See https://developer.apple.com/documentation/swift/rawrepresentable/1540698-rawvalue
The default (Nothing
) will not
include any rawValue.
Typically, if the type does have
a $sel:rawValue:SwiftStruct
, the Ty
will be
I
or Str
.
Note: Currently, nothing will prevent you from putting something nonsensical here.
$sel:typeAlias:Options :: Options -> Bool Source #
Whether or not to generate a newtype as
a type alias. Consider if you want this
or to use getShwiftyWithTags
instead.
The default (False
) will generate newtypes
as their own structs.
$sel:newtypeTag:Options :: Options -> Bool Source #
Whether or not to generate a newtype as an empty enum with a tag. This is for type safety reasons, but with retaining the ability to have Codable conformance.
The default (False
) will not do this.
Note: This takes priority over $sel:typeAlias:Options
.
Note: This option is not currently supported for newtype instances.
Examples
newtype NonEmptyText = MkNonEmptyText String $(getShwiftyWith (defaultOptions { newtypeTag = True }) ''NonEmpyText)
enum NonEmptyTextTag { typealias NonEmptyText = Tagged<NonEmptyTextTag, String> }
$sel:lowerFirstCase:Options :: Options -> Bool Source #
Whether or not to lower-case the first character of a case after applying all modifiers to it.
The default (True
) will do so.
$sel:lowerFirstField:Options :: Options -> Bool Source #
Whether or not to lower-case the first character of a field after applying all modifiers to it.
The default (True
) will do so.
Default Options
defaultOptions :: Options Source #
The default Options
.
defaultOptions :: Options defaultOptions = Options { typeConstructorModifier = id , fieldLabelModifier = id , constructorModifier = id , optionalExpand= False , indent = 4 , generateToSwift = True , generateToSwiftData = True , dataProtocols = [] , dataRawValue = Nothing , typeAlias = False , newtypeTag = False , lowerFirstField = True , lowerFirstCase = True }
Pretty-printing
Functions
prettySwiftData :: ToSwiftData a => Proxy a -> String Source #
Pretty-print a type as its SwiftData
.
Re-exports
A filler type to be used when pretty-printing. The codegen used by shwifty doesn't look at at what a type's type variables are instantiated to, but rather at the type's top-level definition. However, to make GHC happy, you will have to fill in type variables with unused types. To get around this, you could also use something like `-XQuantifiedConstraints`, or existential types, but we leave that to the user to handle.