[Haskell-beginners] Recursive update of Records with IO

Gnani gnani.swami at gmail.com
Mon Oct 10 16:43:28 CEST 2011


Daniel,

Excellent explanation. Thanks a lot.

Mohan.

Sent from my iPad

On Oct 10, 2011, at 20:50, Daniel Fischer <daniel.is.fischer at googlemail.com> wrote:

> On Monday 10 October 2011, 13:53:17, Alia wrote:
>> Hi folks,
>> 
>> 
>> 
>> (Apologies if there is duplication, I didn't see my post archived so I
>> assumed I had to subscribe to be able to post)
>> 
>> 
>> In the hope of tapping the collective wisdom, I'd like to share a
>> problem which seems to be worth sharing: how to recursively update a
>> single record instance with a list of IO tainted functions.
>> 
>> A simple pure IO-less version of what I am trying to do is probably the
>> best way to explain this:
>> 
>> <pure_version>
>> 
>> module Test
>> 
>> where
>> 
>> data Person = Person {name :: String, age :: Int} deriving Show
>> 
>> setName :: String -> Person -> Person
>> setName s p = p {name=s}
>> 
>> setAge :: Int -> Person -> Person
>> setAge i p = p {age=i}
>> 
>> update :: [Person -> Person] -> Person -> Person
>> update [] p  = p
> 
>> update
>> [f] p = f p
> 
> This clause is unnecessary, you can omit it.
> 
>> update (f:fs) p = update fs p'
>>    where
>>        p' = f p
>> 
>> p1 = Person {name="sue", age=12}
>> p2 = update [(setName "sam"), (setAge 32)] p1
>> 
>> </pure_version>
>> 
>> This works very nicely.
> 
> Note that update is a special case of a very general pattern, a fold.
> In this case a left fold, since you're combining the functions with the 
> person in the order the functions appear in the list.
> 
> Prelude> :t foldl
> foldl :: (a -> b -> a) -> a -> [b] -> a
> 
> The type of your list elements is b = (Person -> Person),
> the initial value has type a = Person.
> 
> So what's missing to use foldl is the function of type
> (a -> b -> a), here (Person -> (Person -> Person) -> Person).
> Now, to combine a Person with a (Person -> Person) function to yield a 
> Person, there are two obvious choices,
> 1. const: const p f = p  -- obviously not what we want here
> 2. application: app p f = f p -- i.e. app = flip ($).
> 
> We can define a pretty operator for that,
> 
> infixl 0 |>
> 
> (|>) :: a -> (a -> b) -> b
> x |> f = f x
> 
> and get
> 
> update fs p = foldl (|>) p fs
> 
> Note: foldl is almost never what you want, instead you want foldl' from 
> Data.List, see e.g.
> http://www.haskell.org/haskellwiki/Foldr_Foldl_Foldl%27
> But it doesn't matter here, as Person is defined, foldl' won't give you 
> enough strictness if the list of functions is long; but the list of person-
> updaters will surely be short in general.
> 
> For completeness, the type of the right-fold, foldr:
> 
> Prelude> :t foldr
> foldr :: (a -> b -> b) -> b -> [a] -> b
> 
>> 
>> Now if the setter functions involve some IO, I believe the type
>> signatures should probably look like this:
>> 
>> setName :: String -> Person -> IO Person
>> setAge :: Int -> Person -> IO Person
>> update :: [Person -> IO Person] -> Person -> IO Person
>> 
>> and the setter functions should look like this for example:
>> 
>> setName :: String -> Person -> IO Person
>> setName s p = do
>>    putStrLn "setting name"
>>    return p {name=s}
>> 
>> setAge :: Int -> Person -> IO Person
>> setAge i p = do
>>    putStrLn "setting age"
>>    return p {age=i}
>> 
>> but I'm stuck on how
>> the update function would look.. Any help would be much appreciated.
> 
> We can closely follow the pure case,
> 
> update [] p = return p  -- nothing else we could do
> update (f:fs) p = do
>  p' <- f p
>  update fs p'
> 
> The main difference is that instead of binding the updated person with a 
> where or let, as in the pure case, we bind it in the IO-monad, since that's 
> f's result type.
> 
> Instead of coding the recursion ourselves, we can also use a standard 
> combinator here,
> 
> Prelude Control.Monad> :i foldM
> foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
>        -- Defined in Control.Monad
> 
> The Monad here is IO, a again is Person, and b is now
> (Person -> IO Person).
> 
> For the combinator
> Person -> (Person -> IO Person) -> IO Person
> there are again two obvious possibilities,
> 1. return -- analogous to const
> 2. application, (|>)
> 
> and the IO-version becomes
> 
> import Control.Monad
> 
> update fs p = foldM (|>) p fs
> 
> 
> _______________________________________________
> Beginners mailing list
> Beginners at haskell.org
> http://www.haskell.org/mailman/listinfo/beginners



More information about the Beginners mailing list