Difference between revisions of "Partibles for composing monads"

From HaskellWiki
Jump to: navigation, search
m
(Blanked the page)
Line 1: Line 1:
[[Category:Proposals]]
 
 
 
<blockquote><tt>Having praised monads to the hilt, let me level one criticism. Monads tend to be<br>an all-or-nothing proposition. If you discover that you need interaction deep within<br>your program, you must rewrite that segment to use a monad. If you discover<br>that you need two sorts of interaction, you tend to make a single monad support<br>both sorts. It seems to me that instead we should be able to move smoothly from<br>no monads (no interactions) to one monad (a single form of interaction) to many<br>monads (several independent forms of interactions). How to achieve this remains a<br>challenge for the future.</tt></blockquote>
 
 
* [https://wiki.ittc.ku.edu/lambda/images/3/3b/Wadler_-_How_to_Declare_an_Imperative.pdf ''How to Declare an Imperative''], Philip Wadler.
 
 
Some initial definitions:
 
 
<haskell>
 
class Partible a where
 
part :: a -> (a, a)
 
parts :: a -> [a]
 
 
-- Minimal complete definition: part or parts
 
part u = case parts u of u1:u2:_ -> (u1, u2)
 
parts u = case part u of (u1, u2) -> u1 : parts u2
 
 
instance Partible a => Monad ((->) a) where
 
return x = \ u -> part u `seq` x
 
m >>= k = \ u -> case part u of (u1, u2) -> (\ x -> x `seq` k x u2) (m u1)
 
m >> w = \ u -> case part u of (u1, u2) -> m u1 `seq` w u2
 
fail s = \ u -> part u `seq` error s
 
 
data OI -- abstract
 
primPartOI :: OI -> (OI, OI) -- primitive
 
-- type IO a = OI -> a
 
 
instance Partible OI where part = primPartOI
 
 
 
-- more primitives
 
primGetChar :: OI -> Char
 
primPutChar :: Char -> OI -> ()
 
 
-- copy 'n' paste from Wadler's paper
 
type Dialogue = [Response] -> [Request]
 
data Request = Getq | Putq Char
 
data Response = Getp Char | Putp
 
 
 
respond :: Request -> OI -> Response
 
respond Getq = primGetChar >>= return . Getp
 
respond (Putq c) = primPutChar c >> return Putp
 
 
runDialogue :: Dialogue -> OI -> ()
 
runDialogue d =
 
\ u -> foldr seq () (fix (\ l -> zipWith respond (d l) (parts u)))
 
 
-- fix f = f (fix f)
 
 
 
instance Partible a => MonadFix ((->) a) where
 
mfix m = \ u -> fix (\ x -> m x u)
 
 
 
-- to be made into an abstract data type...
 
data Fresh a = Fresh (OI -> a) OI
 
 
afresh :: (OI -> a) -> OI -> Fresh a
 
afresh g u = Fresh g u
 
 
instance Partible (Fresh a) where
 
part (Fresh g u) = case part u of (u1, u2) -> (Fresh g u1, Fresh g u2)
 
 
fresh :: Fresh a -> [a]
 
fresh u = [ g v | Fresh g v <- parts u ]
 
 
instance Functor Fresh where
 
fmap f (Fresh g u) = Fresh (f . g) u
 
 
 
-- one more primitive
 
primGensym :: OI -> Int
 
 
supplyInts :: OI -> Fresh Int
 
supplyInts = \ u -> afresh primGensym u
 
 
 
instance (Partible a, Partible b) => Partible (a, b) where
 
part (u, v) = case (part u, part v) of
 
((u1, u2), (v1, v2)) -> ((u1, v1), (u2, v2))
 
 
instance (Partible a, Partible b) => Partible (Either a b) where
 
part (Left u) = case part u of (u1, u2) -> (Left u1, Left u2)
 
part (Right v) = case part v of (v1, v2) -> (Right v1, Right v2)
 
 
data Some a = Only a | More a (Some a)
 
 
instance Partible a => Partible (Some a) where
 
part (Only u) = case part u of
 
(u1, u2) -> (Only u1, Only u2)
 
part (More u us) = case part u of
 
(u1, u2) ->
 
case part us of
 
(us1, us2) -> (More u1 us1, More u2 us2)
 
 
type M1 a = (Fresh Int, OI) -> a
 
type M2 a = Either (Fresh a) OI -> a
 
type M3 a = Some (Either (Fresh Char) (Fresh Int)) -> a
 
-- ...whatever suits the purpose
 
 
 
class (Monad m1, Monad m2) => MonadCommute m1 m2 where
 
mcommute :: m1 (m2 a) -> m2 (m1 a)
 
 
instance (Partible a, Partible b) => MonadCommute ((->) a) ((->) b) where
 
mcommute m = \ v u -> m u v
 
</haskell>
 
 
 
So what qualifies as being partible?
 
 
A partible value can be used only once to generate new values that can be used for the same purpose. Think of a very large sheet of paper - new sheets can be made from it, other sheets can be made from those, etc, with the original sheet no longer in existence. Unlike paper sheets, partible values are intended to have no limits e.g. the result of applying <code>supplyInts</code>.
 
 
If its violation causes a runtime error, the use-once property of partible values can help to maintain referential transparency in the effectful segments of a program; using another example from Wadler's paper minimally rewritten in Haskell syntax using <code>OI</code> values:
 
 
<haskell>
 
\ u -> let
 
x = (primPutChar 'h' u `seq` primPutChar 'a' u)
 
in x `seq` x
 
</haskell>
 
 
 
would trigger the error; the working version being:
 
 
<haskell>
 
let
 
x = (\ v -> case part v of
 
(v1, v2) -> primPutChar 'h' v1 `seq` primPutChar 'a' v2)
 
in
 
\ u -> case part u of
 
(u1, u2) -> x u1 `seq` x u2
 
</haskell>
 
 
 
...rather tedious, if it weren't for Haskell's standard monadic methods:
 
 
<haskell>
 
let
 
x = primPutChar 'h' >> primPutChar 'a'
 
in x >> x
 
</haskell>
 
 
 
Higher-order functions allows the manipulation of control e.g. <code>Prelude.until</code> in Haskell. As the definition of <code>runDialogue</code> shows, monadic types with visible definitions based on types of partible values may also allow the manipulation of control in ways beyond what the standard monadic methods provide.
 
 
 
Other references and articles:
 
 
* [https://maartenfokkinga.github.io/utwente/mmf2001c.pdf ''An alternative approach to I/O''], Maarten Fokkinga and Jan Kuper.
 
 
* ''Functional Pearl: On generating unique names'', Lennart Augustsson, Mikael Rittri and Dan Synek.
 
 
* [http://conal.net/blog/posts/can-functional-programming-be-liberated-from-the-von-neumann-paradigm ''Can functional programming be liberated from the von Neumann paradigm?''], Conal Elliott.
 
 
* [http://xenon.stanford.edu/~blynn/haskell/io.html ''Haskell - A Problem With I/O''], Ben Lynn.
 
 
* [http://www.alsonkemp.com/haskell/reflections-on-leaving-haskell ''Reflections on leaving Haskell''], Alson Kemp.
 
 
* [https://www.slideshare.net/nobsun/lazy-io ''Non-Imperative Functional Programming''], Nobuo Yamashita.
 
 
* [http://h2.jaguarpaw.co.uk/posts/mtl-style-for-free ''MTL style for free''], Tom Ellis.
 
 
* ''Functional I/O Using System Tokens'', Lennart Augustsson.
 
 
* ''I/O Trees and Interactive Lazy Functional Programming'', Samuel A. Rebelsky.
 
 
 
Thank you to those who commented on early drafts of this document.
 
 
[[User:Atravers|Atravers]] 04:31, 10 April 2018 (UTC)
 

Revision as of 23:42, 24 December 2018