Send Beginners mailing list submissions to
        [email protected]

To subscribe or unsubscribe via the World Wide Web, visit
        http://www.haskell.org/mailman/listinfo/beginners
or, via email, send a message with subject or body 'help' to
        [email protected]

You can reach the person managing the list at
        [email protected]

When replying, please edit your Subject line so it is more specific
than "Re: Contents of Beginners digest..."


Today's Topics:

   1.  Initialize a data type in (Writer (Endo a))      monoid
      (Dmitriy Matrosov)
   2. Re:  Initialize a data type in (Writer (Endo a)) monoid
      (Brent Yorgey)


----------------------------------------------------------------------

Message: 1
Date: Mon, 13 Jan 2014 21:54:55 +0400
From: Dmitriy Matrosov <[email protected]>
To: [email protected]
Subject: [Haskell-beginners] Initialize a data type in (Writer (Endo
        a))     monoid
Message-ID: <[email protected]>
Content-Type: text/plain; charset=US-ASCII

Hi.

I have the following data type

> import Data.Monoid
> import Control.Monad.Writer

> data Graph    = Graph {graphTitle :: String, graphPoints :: [Int]}
>   deriving (Show)
> emptyGraph :: Graph
> emptyGraph    = Graph {graphTitle = "", graphPoints = []}

Then i want to initialize it in Writer monad using (Dual (Endo Graph))
as monoid:

> type GraphM t = WriterT (Dual (Endo Graph)) t
>
> myGraph :: Monad t => GraphM t [Int]
> myGraph        = do
>                   tell (setTitle "ab")
>                   tell (setTitle "ABCD")
>                   tell (modifyPoints (1 :))
>                   tell (modifyPoints (2 :))
>                   return [1, 2]

and then i can use it e.g. like

> getGraph :: Monad t => (GraphM t a) -> t Graph
> getGraph m    = do
>                   g <- execWriterT m
>                   return (appEndo (getDual g) emptyGraph)
>
> printGraph :: IO ()
> printGraph    = getGraph myGraph >>= print

And to make this work i need two functions implemented for each
record of Graph:

    get :: Graph -> a
    set :: a -> Graph -> Graph

I can define per-record instances of them, like

> setTitle :: String -> Dual (Endo Graph)
> setTitle x    = Dual . Endo $ \g -> g{graphTitle = x}
>
> modifyPoints :: ([Int] -> [Int]) -> Dual (Endo Graph)
> modifyPoints f = Dual . Endo $ \g -> let xs = graphPoints g
>                                      in  g{graphPoints = f xs}

but if Graph has many records, for most of them these 'set' functions ('get'
functions i'll have from records) will look very similar. Is there a way how i
can define all of them "in one row", i.e. using some generic 'set'
implementation to which i should only pass e.g. record name or smth else?
I.e. so, that above myGraph will look like

    ..
    tell(set r x)
    ..

where r is record name (or smth else referencing particulat record) and x is
value.


------------------------------

Message: 2
Date: Mon, 13 Jan 2014 14:07:27 -0500
From: Brent Yorgey <[email protected]>
To: The Haskell-Beginners Mailing List - Discussion of primarily
        beginner-level topics related to Haskell <[email protected]>
Subject: Re: [Haskell-beginners] Initialize a data type in (Writer
        (Endo a)) monoid
Message-ID: <[email protected]>
Content-Type: text/plain; charset=us-ascii

On Mon, Jan 13, 2014 at 09:54:55PM +0400, Dmitriy Matrosov wrote:
> 
> but if Graph has many records, for most of them these 'set' functions ('get'
> functions i'll have from records) will look very similar. Is there a way how i
> can define all of them "in one row", i.e. using some generic 'set'
> implementation to which i should only pass e.g. record name or smth else?
> I.e. so, that above myGraph will look like
> 
>     ..
>     tell(set r x)
>     ..

Yes, you can do this with the 'lens' package.  The package is big and
complicated, but here's all you need to know:

0) import Control.Lens

1) Name your fields with underscores:

> data Graph    = Graph { _graphTitle :: String, _graphPoints :: [Int] }
>   deriving (Show)

2) Add the line

> $(makeLenses ''Graph)

after the definition of Graph (also be sure the TemplateHaskell
extension is enabled)

3) Now you can use the 'view' and 'set' functions (or their infix
equivalents, (^.) and (.~)), like so:

  ..
  tell (set graphTitle x)
  ..

or

  tell (graphTitle .~ x)

makeLenses generated a special lens called 'graphTitle' from the field
name '_graphTitle'.

Of course the above doesn't actually typecheck since (set graphTitle x)
is a function but you need a Dual Endo, but you can easily make your
own custom set function that adds the Dual Endo wrapper, etc.

-Brent


------------------------------

Subject: Digest Footer

_______________________________________________
Beginners mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/beginners


------------------------------

End of Beginners Digest, Vol 67, Issue 17
*****************************************

Reply via email to