Moat
mobile type (currently Swift, Kotlin) generation from Haskell types
Install / Use
/learn @MercuryTechnologies/MoatREADME
moat
Given Haskell Types Generate Equivalents in Your Frontend Language
-- This extension generates Haskell code
{-# LANGUAGE TemplateHaskell #-}
-- These extensions for the generated code to compile
{-# LANGUAGE ScopedTypeVariables, DataKinds #-}
import Moat (mobileGen)
data User =
User
{ firstName :: String
, lastName :: String
, age :: Int
}
$(mobileGen ''User)
The function mobileGen will build two instances for you: ToMoatType and ToMoatData.
Using these instance we can generate code for your favorite language!
{-# LANGUAGE TypeApplications #-}
import Moat (prettySwiftData)
import Data.Proxy (Proxy (..))
generatedSwiftCode :: String
generatedSwiftCode = prettySwiftData . toMoatData $ Proxy @User
Would generate the following Swift code
struct User {
let firstName: String
let lastName: String
let age: Int
}
Motivation
At Mercury, we want a single source of truth for all the types in our
applications. Haskell has a very strong type system and therefore we choose
this as our source of truth. Most programming languages have libraries or
built-ins to deserialize JSON, e.g. Parcelize in Android (Kotlin) and
Codable in Swift, which we use as our interchange format. This allows us to
automatically convert our Haskell types to the frontend equivalents and be sure
everyone is on the same page.

Here is a more practical Swift example that uses Codable to decode JSON
blobs.
import Moat (mobileGenWith, defaultOptions, Options (..))
$(mobileGenWith defaultOptions {dataProtocols = [Codable]} ''User)
which will generate the following Swift code,
struct User: Codable {
let firstName: String
let lastName: String
let age: Int
}
For Android, one can use the kotlin-parcelize package (using prettyKotlinData), i.e.
import Moat (mobileGenWith, defaultOptions, Options (..))
$(mobileGenWith defaultOptions {dataInterfaces = [Parcelable], dataAnnotations = [Parcelize]} ''User)
@Parcelize
data class User(
val firstName: String,
val lastName: String,
val age: Int,
) : Parcelable
Development
We use garnix to cache development shells. You can get directions for pulling from that cache via their docs.
FAQ
Why Template Haskell
Template Haskell is already prevalent in our Haskell codebase. It would
increase our compile times drastically to additionally use Generic to derive AST
for each of our Haskell types.
Useful tidbits
To see the TH AST for a given type
:set -XTemplateHaskell
$(stringE . show =<< reifyDatatype ''A)
''A takes a type constructor A and converts it to a Name. We then bind
from the Q monad to generate a string expression via stringE and show it
Related Skills
node-connect
347.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
