# Difference between revisions of "Record access"

From HaskellWiki

(initialize records with accessor functions) |
(read a record from a binary stream) |
||

Line 26: | Line 26: | ||

module RecordAccess where |
module RecordAccess where |
||

− | import Control.Monad.State (MonadState) |
+ | import Control.Monad.State (MonadState, StateT) |

import qualified Control.Monad.State as State |
import qualified Control.Monad.State as State |
||

+ | import Data.Char (ord) |
||

{- | |
{- | |
||

Line 88: | Line 89: | ||

modifyState :: MonadState r m => Accessor r a -> (a -> a) -> m () |
modifyState :: MonadState r m => Accessor r a -> (a -> a) -> m () |
||

modifyState f g = State.modify (modify f g) |
modifyState f g = State.modify (modify f g) |
||

+ | |||

+ | |||

+ | |||

+ | {- * Reading records from streams -} |
||

+ | |||

+ | class ReadBin a where |
||

+ | readBin :: String -> Maybe (a, String) |
||

+ | |||

+ | instance ReadBin Char where |
||

+ | readBin (c:cs) = Just (c,cs) |
||

+ | readBin _ = Nothing |
||

+ | |||

+ | instance ReadBin Int where |
||

+ | readBin (c0:c1:c2:c3:cs) = |
||

+ | Just (foldl1 (\acc d -> acc*256+d) (map ord [c0,c1,c2,c3]), cs) |
||

+ | readBin _ = Nothing |
||

+ | |||

+ | type Parser r = (r, String) -> Maybe (r, String) |
||

+ | |||

+ | readField :: ReadBin a => Accessor r a -> Parser r |
||

+ | readField f (r,s) = |
||

+ | do (x,s') <- readBin s |
||

+ | return (set f x r, s') |
||

+ | |||

+ | readRecord :: [Parser r] -> Parser r |
||

+ | readRecord ps = flip (foldl (>>=)) ps . Just |
||

Line 103: | Line 130: | ||

− | {- * Example |
+ | {- * Example accesses -} |

{- | Example of using 'set', 'get', 'modify'. -} |
{- | Example of using 'set', 'get', 'modify'. -} |
||

Line 124: | Line 151: | ||

(undefined,undefined) |
(undefined,undefined) |
||

-- setMany [first 'b', second 7] (undefined,undefined) |
-- setMany [first 'b', second 7] (undefined,undefined) |
||

+ | |||

+ | exampleRead :: Maybe ((Char,Int), String) |
||

+ | exampleRead = |
||

+ | readRecord |
||

+ | [readField first, readField second] |
||

+ | ((undefined,undefined), "c\059\154\202\000") |
||

</haskell> |
</haskell> |
||

## Revision as of 09:46, 4 December 2006

Here some proposal for desugared fine functional record field access for HaskellTwo and above.

```
{- |
In Haskell 98 the name of a record field
is automatically also the name of a function which gets the value
of the according field.
E.g. if we have
@
data Pair a b = Pair {first :: a, second :: b}
@
then
@
first :: Pair a b -> a
second :: Pair a b -> b
@
However for setting or modifying a field value
we need to use some syntactic sugar, which is often clumsy.
@
modifyFirst :: (a -> a) -> (Pair a b -> Pair a b)
modifyFirst f r@(Pair {first=a}) = r{first = f a}
@
We propose to extend the meaning of the record field names
to a function which allows setting, getting and modifying values easily.
-}
module RecordAccess where
import Control.Monad.State (MonadState, StateT)
import qualified Control.Monad.State as State
import Data.Char (ord)
{- |
The access functions we propose, look very similar to those
needed for List.mapAccumL (but parameter order is swapped) and State monad.
They get the new value of the field and the record
and return the old value of the field and the record with the updated field.
-}
type Accessor r a = a -> r -> (a, r)
{- *
Access helper functions,
these are similar to State methods and should be in Prelude
-}
{- | Set the value of a field. -}
set :: Accessor r a -> a -> r -> r
set f x = snd . f x
{- | Set many fields at once.
This function could also be used for initialisation of record,
if record value with undefined fields is provided.
Drawback:
Since all types in a list must have the same type,
you can set only values of the same type.
-}
setMany :: [r -> (a, r)] -> r -> r
setMany = flip (foldl (\x f -> snd (f x)))
{- |
This is a general function,
but it is especially useful for setting many values of different type at once.
-}
compose :: [r -> r] -> r -> r
compose = flip (foldl (flip id))
{- | Get the value of a field. -}
get :: Accessor r a -> r -> a
get f = fst . f undefined
{- | Transform the value of a field by a function. -}
modify :: Accessor r a -> (a -> a) -> (r -> r)
modify f g rOld =
let (a,rNew) = f (g a) rOld
in rNew
{- *
Access helper functions in a State monad.
-}
setState :: MonadState r m => Accessor r a -> a -> m ()
setState f x = State.modify (set f x)
getState :: MonadState r m => Accessor r a -> m a
getState f = State.gets (get f)
modifyState :: MonadState r m => Accessor r a -> (a -> a) -> m ()
modifyState f g = State.modify (modify f g)
{- * Reading records from streams -}
class ReadBin a where
readBin :: String -> Maybe (a, String)
instance ReadBin Char where
readBin (c:cs) = Just (c,cs)
readBin _ = Nothing
instance ReadBin Int where
readBin (c0:c1:c2:c3:cs) =
Just (foldl1 (\acc d -> acc*256+d) (map ord [c0,c1,c2,c3]), cs)
readBin _ = Nothing
type Parser r = (r, String) -> Maybe (r, String)
readField :: ReadBin a => Accessor r a -> Parser r
readField f (r,s) =
do (x,s') <- readBin s
return (set f x r, s')
readRecord :: [Parser r] -> Parser r
readRecord ps = flip (foldl (>>=)) ps . Just
{- * Example accessors for the pair type -}
{- | Access to the first value of a pair. -}
first :: Accessor (a,b) a
first xNew (xOld,y) = (xOld, (xNew,y))
{- | Access to the second value of a pair. -}
second :: Accessor (a,b) b
second yNew (x,yOld) = (yOld, (x,yNew))
{- * Example accesses -}
{- | Example of using 'set', 'get', 'modify'. -}
example :: Int
example =
get second $
modify second succ $
set first 'a' $
('b',7)
exampleState :: State.State (Char,Int) Int
exampleState =
do setState first 'a'
modifyState second succ
getState second
exampleInit :: (Char,Int)
exampleInit =
compose [set first 'b', modify first succ, set second 7]
(undefined,undefined)
-- setMany [first 'b', second 7] (undefined,undefined)
exampleRead :: Maybe ((Char,Int), String)
exampleRead =
readRecord
[readField first, readField second]
((undefined,undefined), "c\059\154\202\000")
```