The “reader” monad

后端 未结 3 1312
感动是毒
感动是毒 2021-02-07 06:20

OK, so the writer monad allows you to write stuff to [usually] some kind of container, and get that container back at the end. In most implementations, the \"container\" can act

3条回答
  •  情歌与酒
    2021-02-07 06:49

    The dual of a monoid is a comonoid. Recall that a monoid is defined as (something isomorphic to)

     class Monoid m where
        create :: () -> m
        combine :: (m,m) -> m
    

    with these laws

     combine (create (),x) = x
     combine (x,create ()) = x
     combine (combine (x,y),z) = combine (x,combine (y,z))
    

    thus

     class Comonoid m where
        delete :: m -> ()
        split :: m -> (m,m)
    

    some standard operations are needed

     first :: (a -> b) -> (a,c) -> (b,c)
     second :: (c -> d) -> (a,c) -> (a,d)
    
     idL :: ((),x) -> x
     idR :: (x,()) -> x
    
     assoc :: ((x,y),z) -> (x,(y,z))
    

    with laws like

    idL $ first delete $ (split x) = x
    idR $ second delete $ (split x) = x
    assoc $ first split (split x) = second split (split x)
    

    This typeclass looks weird for a reason. It has an instance

    instance Comonoid m where
       split x = (x,x)
       delete x = ()
    

    in Haskell, this is the only instance. We can recast reader as the exact dual of writer, but since there is only one instance for comonoid, we get something isomorphic to the standard reader type.

    Having all types be comonoids is what makes the category "Cartesian" in "Cartesian Closed Category." "Monoidal Closed Categories" are like CCCs but without this property, and are related to substructural type systems. Part of the appeal of linear logic is the increased symmetry that this is an example of. While, having substructural types allows you to define comonoids with more interesting properties (supporting things like resource management). In fact, this provides a framework for understand the role of copy constructors and destructors in C++ (although C++ does not enforce the important properties because of the existence of pointers).

    EDIT: Reader from comonoids

    newtype Reader r x = Reader {runReader :: r -> x}
    forget :: Comonoid m => (m,a) -> a
    forget = idL . first delete
    
    instance Comonoid r => Monad (Reader r) where
       return x = Reader $ \r -> forget (r,x)
       m >>= f = \r -> let (r1,r2) = split r in runReader (f (runReader m r1)) r2
    
    ask :: Comonoid r => Reader r r
    ask = Reader id
    

    note that in the above code every variable is used exactly once after binding (so these would all type with linear types). The monad law proofs are trivial, and only require the comonoid laws to work. Hence, Reader really is dual to Writer.

提交回复
热议问题