first-class-families: First-class type families

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]

A library for type-level programming.

See README.


[Skip to Readme]

Properties

Versions 0.1.0.0, 0.2.0.0, 0.3.0.0, 0.3.0.1, 0.4.0.0, 0.5.0.0, 0.6.0.0, 0.7.0.0, 0.8.0.0, 0.8.0.1, 0.8.1.0, 0.8.2.0, 0.8.2.0
Change log CHANGELOG.md
Dependencies base (>=4.9 && <5) [details]
License MIT
Copyright 2018-2025 Li-yao Xia
Author Li-yao Xia
Maintainer lysxia@gmail.com
Category Other
Home page https://github.com/Lysxia/first-class-families#readme
Source repo head: git clone https://github.com/Lysxia/first-class-families
Uploaded by lyxia at 2025-10-12T09:40:43Z

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for first-class-families-0.8.2.0

[back to package description]

First-class type families Hackage Build Status

First-class type families are type-level functions that can be composed using higher-order functions.

The core of the idea is an extensible kind of "type-level expressions" and an open type family for evaluating such expressions.

type Exp (k :: Type) :: Type
type family Eval (e :: Exp k) :: k

This library provides that core foundation, and also exports basic first-class type families.

Example

For example, consider this simple type family:

type family   FromMaybe (a :: k) (m :: Maybe k) :: k
type instance FromMaybe a 'Nothing  = a
type instance FromMaybe a ('Just b) = b

With first-class-families (fcfs), it translates to a data declaration and instances for a single Eval family:

import Fcf

data FromMaybe :: k -> Maybe k -> Exp k
type instance Eval (FromMaybe a 'Nothing)  = a
type instance Eval (FromMaybe a ('Just b)) = b

That way, the FromMaybe constructor can be partially applied, and passed to higher-order fcfs such as Map:

Eval (Map (FromMaybe 0) '[ 'Just 1, 'Nothing ])  =  '[ 1, 0 ] :: [Nat]

Essential language extensions:

{-# LANGUAGE
    DataKinds,
    PolyKinds,
    TypeFamilies,
    TypeInType,
    TypeOperators,
    UndecidableInstances #-}

Overview

The top-level module Fcf is a prelude to get acquainted with the library. For regular use, import what you need from the specialized modules above, preferably with explicit import lists.

import Fcf                       -- Simple but fragile

import Fcf.Class.Functor (FMap)  -- Explicit and robust

Features

Overloaded type families

Value-level functions can be overloaded using type classes. Type families---type-level functions---are open by design, so overloading is as easy as just declaring them with more general types.

data Map :: (a -> Exp b) -> f a -> Exp (f b)

-- Instances for f = []
type instance Eval (Map f '[]) = '[]
type instance Eval (Map f (x ': xs)) = Eval (f x) ': Eval (Map f xs)

-- Instances for f = Maybe
type instance Eval (Map f 'Nothing) = 'Nothing
type instance Eval (Map f ('Just x)) = 'Just (Eval (f x))

See also


Contributions are welcome. Feel free to open an issue or make a PR on Github!