TypeScript code generation from a haskell-like syntax for ADT (algebraic data types)
A playground with a few examples
To install the stable version:
npm install fp-ts-codegen
Signature
function run(input: string, options: Options = defaultOptions): Either<string, string>
Example
import { run } from 'fp-ts-codegen'
console.log(run('data Option A = None | Some A'))
Output
/** type definition */
export type Option<A> =
| {
readonly type: 'None'
}
| {
readonly type: 'Some'
readonly value0: A
}
/** constructors */
export const none: Option<never> = { type: 'None' }
export function some<A>(value0: A): Option<A> {
return { type: 'Some', value0 }
}
/** pattern matching */
// eager ---v
export function fold<A, R>(fa: Option<A>, onNone: R, onSome: (value0: A) => R): R {
switch (fa.type) {
case 'None':
return onNone
case 'Some':
return onSome(fa.value0)
}
}
// lazy ---v
export function foldL<A, R>(fa: Option<A>, onNone: () => R, onSome: (value0: A) => R): R {
switch (fa.type) {
case 'None':
return onNone()
case 'Some':
return onSome(fa.value0)
}
}
/** prisms */
import { Prism } from 'monocle-ts'
export function _none<A>(): Prism<Option<A>, Option<A>> {
return Prism.fromPredicate(s => s.type === 'None')
}
export function _some<A>(): Prism<Option<A>, Option<A>> {
return Prism.fromPredicate(s => s.type === 'Some')
}
/** Setoid instance */
import { Setoid } from 'fp-ts/lib/Setoid'
export function getSetoid<A>(setoidSomeValue0: Setoid<A>): Setoid<Option<A>> {
return {
equals: (x, y) => {
if (x === y) {
return true
}
if (x.type === 'None' && y.type === 'None') {
return true
}
if (x.type === 'Some' && y.type === 'Some') {
return setoidSomeValue0.equals(x.value0, y.value0)
}
return false
}
}
}
Syntax: { name :: type }
Example
Source
-- record ---v
data User = User { name :: string, surname :: string }
Output
export type User = {
readonly type: 'User'
readonly name: string
readonly surname: string
}
export function user(name: string, surname: string): User {
return { type: 'User', name, surname }
}
Syntax: (type1, type2, ...types)
Example
Source
-- tuple ---v
data Tuple2 A B = Tuple2 (A, B)
Output
export type Tuple2<A, B> = {
readonly type: 'Tuple2'
readonly value0: [A, B]
}
export function tuple2<A, B>(value0: [A, B]): Tuple2<A, B> {
return { type: 'Tuple2', value0 }
}
Syntax: (<name> :: <constraint>)
Example
Source
-- constraint ---v
data Constrained (A :: string) = Fetching | GotData A
Output
export type Constrained<A extends string> =
| {
readonly type: 'Fetching'
}
| {
readonly type: 'GotData'
readonly value0: A
}
Example
Source
data Option A = None | Some A
Output
declare module 'fp-ts/lib/HKT' {
interface URI2HKT<A> {
Option: Option<A>
}
}
export const URI = 'Option'
export type URI = typeof URI
export type Option<A> = None<A> | Some<A>
export class None<A> {
static value: Option<never> = new None()
readonly _tag: 'None' = 'None'
readonly _A!: A
readonly _URI!: URI
private constructor() {}
fold<R>(onNone: R, _onSome: (value0: A) => R): R {
return onNone
}
foldL<R>(onNone: () => R, _onSome: (value0: A) => R): R {
return onNone()
}
}
export class Some<A> {
readonly _tag: 'Some' = 'Some'
readonly _A!: A
readonly _URI!: URI
constructor(readonly value0: A) {}
fold<R>(_onNone: R, onSome: (value0: A) => R): R {
return onSome(this.value0)
}
foldL<R>(_onNone: () => R, onSome: (value0: A) => R): R {
return onSome(this.value0)
}
}
export const none: Option<never> = None.value
export function some<A>(value0: A): Option<A> {
return new Some(value0)
}
import { Prism } from 'monocle-ts'
export function _none<A>(): Prism<Option<A>, Option<A>> {
return Prism.fromPredicate(s => s.type === 'None')
}
export function _some<A>(): Prism<Option<A>, Option<A>> {
return Prism.fromPredicate(s => s.type === 'Some')
}
// fp-ts-codegen/lib/ast module
export interface Options {
/** the name of the field used as tag */
tagName: string
/** the name prefix used for pattern matching functions */
foldName: string
/** the name used for the input of pattern matching functions */
matcheeName: string
/**
* the pattern matching handlers can be expressed as positional arguments
* or a single object literal `tag -> handler`
*/
handlersStyle: { type: 'positional' } | { type: 'record'; handlersName: string }
encoding: 'literal' | 'fp-ts'
}
export const defaultOptions: Options = {
tagName: 'type',
foldName: 'fold',
matcheeName: 'fa',
handlersStyle: { type: 'positional' },
encoding: 'literal'
}
Example
import { lenses, defaultOptions } from 'fp-ts-codegen/lib/ast'
lenses.tagName.set('tag')(defaultOptions)
/*
{
tagName: 'tag',
foldName: 'fold',
matcheeName: 'fa',
...
}
*/
ast
module: internal model -> TypeScript ASTmodel
module: internal modelprinter
module: internal model -> TypeScript codehaskell
module: haskell-like syntax -> internal modelindex
module: haskell-like syntax -> TypeScript code
- derive type class instances? (
Functor
,Foldable
, etc...) - ???