[Haskell-cafe] Flexible Wrappers - an Introduction

Iain Alexander ia at stryx.demon.co.uk
Sat Mar 12 03:09:14 CET 2011


There are three flexible wrappers provided in the initial flexiwrap package 
[1].  (More may be required in the future.)  

    short   long            wraps
    FW      FlexiWrap       values
    FWT     FlexiWrapT      unary type constructors
    FWCTC   FlexiWrapCTC    binary operators which combine two
                            unary type constructors

The short names are frequently convenient since type signatures can get quite 
long.  The suffixes are a simple encoding of the kind of the wrapped quantity, 
description of which I defer to a later post.  

The first parameter to each of these is a phantom - it plays no part in the 
implementation type, but serves as an index to generate multiple distinct types 
from each wrapper. In addition, by using a type-level list here, we can use it 
to specify any number of instance implementations.  

By way of a rather contrived but simple example, the module 
Data.Flex.SmallCheck.Wrap implements and uses an instance selector called 
FWEqFirst to implement an Eq instance which compares pairs by their first 
element.  It is used there by defining a type  

    FW (FWEqFirst :*: TNil) (Bool, Bool)

which has the requisite properties.

The initial proof-of-concept version of the package provides only a limited 
number of instance selectors (with the associated machinery) for each wrapper.  


FW (in Data.Flex.Wrap) has only an implementation for Eq instances, with 
FWDefaultEq to explicitly specify the default implementation which simply 
delegates to the underlying wrapped type.  

FWT (Data.Flex.WrapT) has

FWTDefaultFunctor (Functor)
FWTDefaultApplicative (Applicative)
FWTDefaultMonadAll (the combination of FWTDefaultMonad and
    FWTDefaultMonadState)
FWTDefaultMonad (Monad)
FWTDefaultMonadState (MonadState)

which all again delegate to the underlying type.

There is also a separate module Data.Flex.WrappedMonad which provides selectors 
to implement Functor and Applicative instances for a Monad which lacks them.  

FWWrapMonad (the combination of FWMonadFunctor and FWMonadApplicative)
FWMonadFunctor (Monad => Functor)
FWMonadApplicative (Monad => Applicative)

FWCTC (Data.Flex.WrapCTC) has

FWCTCDefaultFunctor (Functor)
FWCTCDefaultMonad (Monad)

which simply delegate to the application of the wrapped binary operator to two 
constructor arguments, but provides underlying machinery for these together 
with MonadPlus and MonadTrans.  

Data.Flex.Compose contains the (:.) (alias O) type composition operator as 
found in e.g. the TypeCompose package [2].  It contains a fairly literal 
translation of Mark Jones and Luc Duponcheel's scheme for monad composition 
[3],  

FWCompP - use the "prod" construction
FWCompD - use the "dorp" construction
FWCompS - use the "swap" construction

and selectors for MonadTrans and MonadPlus

FWCompDefaults (the combination of FWCompTrans and FWCompMonadPlus)
FWCompTrans (MonadTrans)
FWCompMonadPlus (a synonym for FWCompMonadPlusR)
FWCompMonadPlusR (using the MonadPlus instance of the right-hand
    argument of the composition)
FWCompMonadPlusL (using the MonadPlus instance of the left-hand
    argument of the composition)

Data.Flex.FlipT contains the operator FlipT which flips the arguments of an 
operator such as (:.).  

FWFlipDefaults (the combination of FWFlipMonad and FWFlipMonadPlus)
FWFlipMonad (Monad)
FWFlipMonadPlus (MonadPlus)

These delegate the implementation to the underlying binary operator, which it 
wraps with FWCTC, passing the phantom selector list along.  

So, to recap the example given in the package release announcement:

data FWStrict = FWStrict

type Strict = FW (FWStrict :*: TNil)

type StrictT = FWCTC
    (FWFlipDefaults :*:
        FWCompMonadPlusL :*: FWCompDefaults :*: FWCompS :*: TNil
    )
    (FlipT O) Strict

defines a (e.g. monad, but in general constructor) transformer StrictT. FWCTC 
is a flexible wrapper.  Its first parameter is an HList-like type-level list of 
instance specifications.  O is the type composition operator, and FlipT flips 
its arguments.  Strict is a user-defined wrapper type. (This is strict vs. 
loose, not strict vs. lazy.  It is intended to be used to wrap values of a data 
structure which satisfy certain criteria.) FWCompMonadPlusL specifies a 
particular MonadPlus instance (which delegates to the left operand of the 
composition, which is the right operand of the flipped composition, i.e. 
whatever argument you pass to StrictT), and FWCompS specifies the 
Jones/Duponcheel SComp construction [3].  The other two items in the list 
(apart from the TNil terminator) specify default implementations of other 
instances.  

[1] http://hackage.haskell.org/package/flexiwrap-0.0.1
[2] http://hackage.haskell.org/package/TypeCompose
[3] http://web.cecs.pdx.edu/~mpj/pubs/RR-1004.pdf




More information about the Haskell-Cafe mailing list