elminator: Generate ELM types/encoders/decoders from Haskell types.

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

Please see the README on GitHub at https://github.com/sras/elminator#readme


[Skip to Readme]

Properties

Versions 0.0.0.0, 0.0.0.0, 0.1.0.0, 0.2.0.0, 0.2.1.0, 0.2.2.0, 0.2.2.1, 0.2.3.0, 0.2.3.1, 0.2.4.0, 0.2.4.1, 0.2.4.2, 0.2.4.3, 0.2.4.4
Change log ChangeLog.md
Dependencies aeson, base (>=4.7 && <5), containers, mtl, template-haskell, text [details]
License BSD-3-Clause
Copyright 2019 Sandeep.C.R
Author Sandeep.C.R
Maintainer sandeep@sras.me
Category Code Generation, Elm
Source repo head: git clone https://github.com/sras/elminator.git
Uploaded by sras at 2019-05-25T09:03:18Z

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for elminator-0.0.0.0

[back to package description]

Elminator

Generate Elm type definitions and json encoders/decoders from Haskell source.

  1. Supports generation of polymorphic types in Elm including types with phantom type variables.
  2. Supports generation of direct and indirect recursive types as long as the recursive types are not polymorphic.
  3. Generates code that does not depend on external Elm libraries.
  4. Does not have limits on the number of fields that the constructors of your type can have.
  5. Supports json encoding options exported by the Aeson library comprehensively.
  6. Supports generation of code that depend on user defined types and encoders/decoders in Elm.

How to use?

To generate Elm code for a Haskell type, the Haskell type needs to have an instance of the ToHType type class. This can be automatically derivied, provided all your constructor field types have ToHType instances. A sample can be seen below. Please note that language extensions DeriveGeneric and DeriveAnyClass should be enabled to make this work.

{-# Language DeriveGeneric #-}
{-# Language DeriveAnyClass #-}

module Lib  where

import Elminator
import GHC.Generics (Generic)

data SingleCon = SingleCon Int String deriving (Generic, ToHType)

Since this library uses template haskell to look up type information (in addition to Generics), we need to run the code generation code in a template Haskell splice. A usage sample can be seen in the following code used in the round trip tests for this library.

{-# Language OverloadedStrings #-}
{-# Language TemplateHaskell #-}

module CodeGen where

import Data.Proxy
import Elminator
import Data.Text.IO
import Data.Text

import Lib

elmSource :: Text
elmSource = $(generateFor Elm19 myDefaultOptions (Just "elm-app/src/Autogen.elm") $ do
  include (Proxy :: Proxy SingleCon) $ Everything Mono
  include (Proxy :: Proxy SingleRecCon) $ Everything Mono
  include (Proxy :: Proxy SingleConOneField)$ Everything Mono
  include (Proxy :: Proxy SingleRecConOneField) $ Everything Mono
  include (Proxy :: Proxy TwoCons) $ Everything Mono 
  include (Proxy :: Proxy TwoRecCons) $ Everything Mono 
  include (Proxy :: Proxy BigCon) $ Everything Mono 
  include (Proxy :: Proxy BigRecCon) $ Everything Mono 
  include (Proxy :: Proxy MixedCons) $ Everything Mono 
  include (Proxy :: Proxy Comment) $ Everything Mono 
  include (Proxy :: Proxy WithMaybes) $ Everything Mono
  include (Proxy :: Proxy WithSimpleMaybes) $ Everything Mono 
  include (Proxy :: Proxy (WithMaybesPoly (Maybe String) Float)) $ Definiton Poly
  include (Proxy :: Proxy (WithMaybesPoly (Maybe String) Float)) $ EncoderDecoder
  include (Proxy :: Proxy (Phantom ())) $ Everything Poly
  include (Proxy :: Proxy (TypeWithPhantom Float)) $ Everything Poly
  include (Proxy :: Proxy RecWithList) $ Everything Mono
  include (Proxy :: Proxy IndRecStart) $ Everything Mono
  include (Proxy :: Proxy IndRec2) $ Everything Mono
  include (Proxy :: Proxy IndRec3) $ Everything Mono
  include (Proxy :: Proxy NTSingleCon) $ Everything Mono
  include (Proxy :: Proxy NTSingleCon2) $ Everything Poly
  include (Proxy :: Proxy Tuples) $ Everything Mono
  include (Proxy :: Proxy NestedTuples) $ Everything Mono
  include (Proxy :: Proxy (TypeWithExt ())) $ Everything Poly
  include (Proxy :: Proxy (WithEmptyTuple ())) $ Everything Poly
  include (Proxy :: Proxy (Phantom2 ())) $ Everything Poly
  include (Proxy :: Proxy PhantomWrapper) $ Everything Poly
  )

-- The `generateFor` function accepts an elm version (only Elm19 as of now), a value of type `Options` from the Aeson library
-- , and optional `FilePath` to which the generated source will be written to, and a `Builder` value.
-- The `Builder` is just a `State` monad that aggregates the configuration parameters from the include
-- calls. The first parameter of the include function is a `proxy` value that denotes the type that requires Elm code generation.
-- The second value is a value of type `GenOption` that selects which entites needs to be generation, and also selects if the
-- type generated at Elm should be polymorphic. It is defined as follows.

data GenOption
  = Definiton PolyConfig  -- Generate Type definition in Elm. PolyConfig field decides if the type has to be polymorphic
  | EncoderDecoder -- Generate Encoder and Decoder in Elm
  | Everything PolyConfig -- Generate both type definition, encoders and decoders. PolyConfig field decides if the type has to be polymorphic.

data PolyConfig
  = Mono | Poly 

The Elm code generated by the above code can be seen here

How to depend on predefined types and encoders/decoders

This is intended to be an escape hatch in cases where the types you want to generate elm code for, inturn contains types that you didn't define, and do not have access to the internals of. This feature basically allows you to define the Elm type and encoders/decoders yourself, and let the generated code import it and use them in the generated code.

To use this, derive the ToHType instance for the type using the HExternal constructor of the HType type. Sample code can be seen below, where we define a ToHType instance for a type called MyExtType.

instance (ToHType a, ToHType b) => ToHType (MyExtType a b) where
  toHType _ = do
    ha <- toHType (Proxy :: Proxy a)
    hb <- toHType (Proxy :: Proxy b)
    pure $
      HExternal
        (ExInfo
          (ExItem "Lib" "MyExtType") 
          (Just $ ExItem "Lib" "encodeMyExtType")
          (Just $ ExItem "Lib" "decodeMyExtType"))
        [ha, hb]

Installing

If you are using stack, then for the time being, you have to add Elminator to the extra deps section as follows.

extra-deps:
  elminator-0.0.0.0