Are the following assertions true:
The only real isomorphism, accessible programatically to the user, verified by Haskell type system, and that the Haskell compiler is/can be made aware of, is between:
- the set of values of a Haskell datatype
- the set of values of types those required by its constructors
Even generic programming can't produce "true" isomorphism, whose composition results at run time in an identity (thus staged-sop - and similarly in Ocaml)
Haskell itself is the only producing isomorphism,
Coercible, but those isomorphism are restricted to the identity isomorphism
By "real isomorphism, accessible programatically to the user, verified by Haskell type system, and that the Haskell compiler is/can be made aware of" I mean a pair of function u : a -> b and v : b -> a such that Haskell knows (by being informed or otherwise) that u.v=id and v.u=id. Just like it knows (at compile time) how to rewrite some code to do "fold fusion", which is akin to, at once, recognize and apply it.
Look into Homotopy Type Theory/Cubical Agda where an "equality is isomorphism". I am not familiar enough with it to know what happens operationally, even if Agda knows isomorphic types are equal I still think your "true isomorphism" (i.e. with a proof and fusion) is too tall of an order.
In GHC it is possible to derive via "isomorphisms" but we need to wait for dependent types to properly verify isomorphisms in the type system. Even so they can be used to produce bone fide code even if you have to do some work operationally.
You already mentioned "representational equality" (
Coercible) but it is worth discussing it. It underpins the twocoerce-based deriving strategies:GeneralizedNewtypeDerivingandDerivingViawhich generalizes GND.GND is the simplest way to turn an isomorphism (
Coercible USD Int) into code:Operationally
coerceis zero-cost at so they incur no cost at run-time. This is the only way you will get what you want in Haskell.Isomorphisms can also be done through user-defined type classes.
An instance of
Representable fmeansfis (naturally) isomorphic to functions from its representing object(Rep f ->). The newtypeCouses this isomorphism to derive function instances for representable functor. APair aof two values is represented byBool, and is thus isomorphic toBool -> a.This isomorphism lets
PairderiveFunctor,ApplicativeandMonadby roundtripping through(Bool ->):When you derive
Generic/Generic1the compiler generates an isomorphism between a generic type and its generic representationRep/Rep1(not to be confused with the representing objectRepfrom the above example).The class laws state that
to/fromandto1/from1witness that isomorphism. The type system does not enforce these laws but if you derive them they should hold.They are the main way to define generic implementations in Haskell. I recently introduced two newtypes
GenericallyandGenerically1to base, as standard names for generic behaviour (use generic-data until the next GHC release). You can derive a generic isomorphism and programmatically use it in the next line without leaving thedatadeclaration:You will however have to pay for the converstion cost, it's not as simple as adding
{-# Rules "to/from" to . from = id #-}because the actual instances will appear with intermediate terms liketo (from a <> from b). Even your "true isomorphisms" GHC could not fuse away the conversion since it's not of the formto . from.There is also a library
iso-deriving(blog) that allows deriving via arbitrary isomorphisms.