# coercible-subtypes This library provides unidirectional (one-way) variant of [Coercion](https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Type-Coercion.html). The variant is a type `Sub` defined in `Data.Type.Coercion.Sub`. `Sub a b` can be used to convert a type `a` to another type `b`. ``` upcastWith :: Sub a b -> a -> b ``` For all `Sub a b` values, the runtime representation of `a` and `b` values are same, so `upcastWith` do not require any computation to return `b` value, just [coerce](https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Coerce.html)s GHC to treat a value of `a` as type `b`. This feature is not different to `Coercion`. The difference is that while `Coercion` represents bidirectional relation, `Sub` represents unidirectional relation. `Coercion a b` and its underlying type class `Coercible a b` witnesse you can coerce both `a` to `b` and `b` to `a`. Unlike that, `Sub a b` only allows you to coerce `a` to `b`, not `b` to `a`. ## Usage Example To use this library effectively, it must be used at two places: a library and its user code. For this example, let's assume they are written by two people, a *library author* and a *user*. The library author writes a module `RightTriangle` below. ``` module RightTriangle(Triangle(), toEdges, getEdges, fromEdges) where import Data.Coerce import Data.Type.Coercion.Sub newtype Triangle = MkTriangle (Int, Int, Int) -- | Triangles can be coerced into 3-tuples of Ints toEdges :: Sub Triangle (Int, Int, Int) toEdges = sub getEdges :: Triangle -> (Int, Int, Int) getEdges = coerce -- | Creates right triangle from lengths of edges (a,b,c) -- -- > * -- > |\ c -- > a| \ -- > *--* -- > b -- -- (a^2 + b^2 == c^2) must hold. fromEdges :: (Int, Int, Int) -> Maybe Triangle fromEdges = {- Omit -} ``` The author wants to protect the invariant condition `a^2 + b^2 == c^2`. For that purpose, the author can't export the constructor of `Triangle`. Because it is symmetric, `Coercion Triangle (Int,Int,Int)` can't be exported either. The user is building an application using `RightTriangle` module. ``` module Main where import Data.Map (Map) import RightTriangle import Data.Type.Coercion.Sub main :: IO () main = ...... ``` In this application, the user has to convert `Map String Triangle` to `Map String (Int, Int, Int)`, revealing the edge lengths of the triangles. While it is easy to do so with `fmap getEdges`, using `fmap` here can make an entire copy of the Map[†](#footnote). This is wasted work and memory. Instead, the user can use `mapR toEdges` to get `Sub (Map String Triangle) (Map String (Int, Int, Int))` and then `upcastWith` to perform zero cost coercion over `Map`. ## Comparison against other methods There are some other methods to achive the goal of this library. * Just give up coercion * This is just for better performance, so not doing it is always an option. * Rewrite rules * Rewrite rules based method is currently employed, and working at our hand. So, it is possible you don't need this library at all. * The downside is whether it works or not is on the provider of the "container" type in use, and GHC doing expected optimizations. Without reading source codes and examining the GHC optimization result (e.g. `-ddump-rule-firings`), you can't be sure you are doing the conversion zero-cost. -------- For `Data.Map`, which [containers](https://hackage.haskell.org/package/containers) package provides, can optimize `fmap` away via proper inlining and rewrite rules. The purpose of this library is turning optimizations into explicit codes, or handling the cases when the container type in use does not provide such an opportunity via rewrite rules.