Copyright | (C) 2011-2015 Edward Kmett |
---|---|
License | BSD-style (see the file LICENSE) |
Maintainer | Edward Kmett <ekmett@gmail.com> |
Stability | provisional |
Portability | portable |
Safe Haskell | Trustworthy |
Language | Haskell2010 |
For a good explanation of profunctors in Haskell see Dan Piponi's article:
http://blog.sigfpe.com/2011/07/profunctors-in-haskell.html
This module includes unsafe composition operators that are useful in practice when it comes to generating optimal core in GHC.
If you import this module you are taking upon yourself the obligation
that you will only call the operators with #
in their names with functions
that are operationally identity such as newtype
constructors or the field
accessor of a newtype
.
- class Profunctor p where
Profunctors
class Profunctor p where #
Formally, the class Profunctor
represents a profunctor
from Hask
-> Hask
.
Intuitively it is a bifunctor where the first argument is contravariant and the second argument is covariant.
You can define a Profunctor
by either defining dimap
or by defining both
lmap
and rmap
.
If you supply dimap
, you should ensure that:
dimap
id
id
≡id
If you supply lmap
and rmap
, ensure:
lmap
id
≡id
rmap
id
≡id
If you supply both, you should also ensure:
dimap
f g ≡lmap
f.
rmap
g
These ensure by parametricity:
dimap
(f.
g) (h.
i) ≡dimap
g h.
dimap
f ilmap
(f.
g) ≡lmap
g.
lmap
frmap
(f.
g) ≡rmap
f.
rmap
g
dimap :: (a -> b) -> (c -> d) -> p b c -> p a d #
lmap :: (a -> b) -> p b c -> p a c #
rmap :: (b -> c) -> p a b -> p a c #
(#.) :: Coercible c b => (b -> c) -> p a b -> p a c infixr 9 #
Strictly map the second argument argument covariantly with a function that is assumed operationally to be a cast, such as a newtype constructor.
Note: This operation is explicitly unsafe
since an implementation may choose to use
unsafeCoerce
to implement this combinator
and it has no way to validate that your function
meets the requirements.
If you implement this combinator with
unsafeCoerce
, then you are taking upon yourself
the obligation that you don't use GADT-like
tricks to distinguish values.
If you import Data.Profunctor.Unsafe you are taking upon yourself the obligation that you will only call this with a first argument that is operationally identity.
The semantics of this function with respect to bottoms should match the default definition:
(#.
) ≡ \f -> \p -> p `seq`rmap
f p
(.#) :: Coercible b a => p b c -> (a -> b) -> p a c infixl 8 #
Strictly map the first argument argument contravariantly with a function that is assumed operationally to be a cast, such as a newtype constructor.
Note: This operation is explicitly unsafe
since an implementation may choose to use
unsafeCoerce
to implement this combinator
and it has no way to validate that your function
meets the requirements.
If you implement this combinator with
unsafeCoerce
, then you are taking upon yourself
the obligation that you don't use GADT-like
tricks to distinguish values.
If you import Data.Profunctor.Unsafe you are taking upon yourself the obligation that you will only call this with a second argument that is operationally identity.
(.#
) ≡ \p -> p `seq` \f ->lmap
f p