https://wiki.haskell.org/index.php?title=Function_decoration_pattern&feed=atom&action=historyFunction decoration pattern - Revision history2016-10-21T20:59:24ZRevision history for this page on the wikiMediaWiki 1.19.14+dfsg-1https://wiki.haskell.org/index.php?title=Function_decoration_pattern&diff=55273&oldid=prevDerek Elkins at 22:36, 20 January 20132013-01-20T22:36:49Z<p></p>
<table class='diff diff-contentalign-left'>
<tr valign='top'>
<td colspan='1' style="background-color: white; color:black;">← Older revision</td>
<td colspan='1' style="background-color: white; color:black;">Revision as of 22:36, 20 January 2013</td>
</tr></table>Derek Elkinshttps://wiki.haskell.org/index.php?title=Function_decoration_pattern&diff=55272&oldid=prevDerek Elkins at 20:53, 20 January 20132013-01-20T20:53:10Z<p></p>
<table class='diff diff-contentalign-left'>
<tr valign='top'>
<td colspan='1' style="background-color: white; color:black;">← Older revision</td>
<td colspan='1' style="background-color: white; color:black;">Revision as of 20:53, 20 January 2013</td>
</tr></table>Derek Elkinshttps://wiki.haskell.org/index.php?title=Function_decoration_pattern&diff=55271&oldid=prevDerek Elkins: New page: Category:Idioms ==Motivation== You want to add extra properties to a function type, but you don't want the users to have to tediously project out the decorated type when they don't c...2013-01-20T20:40:29Z<p>New page: <a href="/Category:Idioms" title="Category:Idioms">Category:Idioms</a> ==Motivation== You want to add extra properties to a function type, but you don't want the users to have to tediously project out the decorated type when they don't c...</p>
<p><b>New page</b></p><div>[[Category:Idioms]]<br />
<br />
==Motivation==<br />
<br />
You want to add extra properties to a function type, but you don't want the users to have to tediously project out the decorated type when they don't care about the decorations.<br />
<br />
This can be generalized to arbitrary values instead of just functions.<br />
<br />
==Approach==<br />
<br />
Use type classes to drive the projection by the how the value is used.<br />
<br />
<haskell><br />
{-# LANGUAGE MultiParamTypeClasses, Rank2Types, FlexibleContexts, FlexibleInstances #-}<br />
<br />
-- This implementation is somewhat general, but it is not intended<br />
-- that all examples can be cast in exactly this way.<br />
data Decorate d a b = Decorated (a -> b) (d a b)<br />
<br />
class Decorated d a b dec where<br />
decorated :: (a -> b) -> d a b -> dec a b <br />
-- The above is a Scott-encoding of the below which is equivalent.<br />
-- The Scott-encoded version is often more convenient and efficient.`<br />
-- decorated :: Decorate d a b -> dec a b<br />
<br />
instance Decorated d a b (Decorate d) where<br />
decorated = Decorated<br />
<br />
instance Decorated d a b (->) where<br />
decorated f _ = f<br />
<br />
type IsDecorated d a b = forall dec. Decorated d a b dec => dec a b <br />
<br />
-- Not a very realistic example.<br />
type UnitTested = Decorate (,)<br />
type IsUnitTested a b = IsDecorated (,) a b<br />
<br />
makeTested :: (a -> b) -> a -> b -> IsUnitTested a b<br />
makeTested f a b = decorated f (a, b)<br />
<br />
test :: Eq b => UnitTested a b -> Bool<br />
test (Decorated f (a, b)) = f a == b<br />
<br />
testedSquare :: Num a => IsUnitTested a a<br />
testedSquare = makeTested (\x -> x * x) 3 9<br />
<br />
main = do<br />
print (map testedSquare [1,2,3])<br />
putStrLn (if test testedSquare then "Passed" else "Failed")<br />
</haskell><br />
<br />
<br />
==Examples==<br />
<br />
The archetypical example is the type of isomorphisms e.g. as used in the [http://hackage.haskell.org/packages/archive/lens/3.7.3/doc/html/Control-Lens-Iso.html lens] library.<br />
<br />
An isomorphism is a function equipped with an inverse. Traditionally, this would be represented by a data type such as<br />
<haskell><br />
data Iso a b = Iso { _to :: a -> b, _from :: b -> a }<br />
</haskell><br />
<br />
This would require explicitly projecting out the forward function using <hask>_to</hask>, which makes the code noisy and tedious to write. This can be eliminated by writing: <br />
<haskell><br />
class Isomorphic a b iso where<br />
iso :: (a -> b) -> (b -> a) -> iso a b<br />
<br />
instance Isomorphic a b Iso where<br />
iso = Iso<br />
<br />
instance Isomorphic a b (->) where<br />
iso to _ = to<br />
<br />
type IsIsomorphic a b = forall iso. Isomorphic a b iso => iso a b<br />
</haskell><br />
<br />
Now if we produce values of type <hask>IsIsomorphic a b</hask> rather than <hask>Iso a b</hask> we can just treat them like functions. Note that this should be pursued aggresively. For example, traditionally we'd have two functions <hask>from :: Iso a b -> b -> a</hask> and <hask>op :: Iso a b -> Iso b a</hask> but by using <hask>from :: Iso a b -> IsIsomorphic b a</hask> we get both at once. This can be enforced by making <hask>Iso</hask> abstract.<br />
<br />
==Notes==<br />
<br />
This is closely related to the Yoneda lemma and representability. Essentially we are identifying the value <hask>x</hask> with <hask>($ x)</hask>. The instances of the type classes just choose how we want to observe the <hask>x</hask> via <hask>($ x) observation</hask>. <hask>makeTested</hask> makes this pretty explicit especially if the direct (rather than Scott-encoded) representation is used.</div>Derek Elkins