[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