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. Re:  Re: Beginners Digest, Vol 29, Issue 41 (Ozgur Akgun)
   2. Re:  Re: Beginners Digest, Vol 29, Issue 41 (J?rgen Doser)
   3. Re:  Re: Beginners Digest, Vol 29, Issue 41 (Stephen Tetley)
   4. Re:  Re: Beginners Digest, Vol 29, Issue 41 (Russ Abbott)
   5.  Learning StateT (Tim Baumgartner)
   6. Re:  Learning StateT (Brent Yorgey)


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

Message: 1
Date: Fri, 26 Nov 2010 20:21:54 +0000
From: Ozgur Akgun <[email protected]>
Subject: Re: [Haskell-beginners] Re: Beginners Digest, Vol 29, Issue
        41
To: [email protected]
Cc: [email protected]
Message-ID:
        <[email protected]>
Content-Type: text/plain; charset="utf-8"

On 26 November 2010 19:50, Russ Abbott <[email protected]> wrote:

> P.S. I'll admit that I've spent virtually no time with monads and would
> prefer to avoid them if possible.
>

Not monad, but 'knot's :)

If you tie the knot while creating those values, then, you don't need to
update the same information in several places.

Have a look: http://haskell.org/wikisnapshot/TyingTheKnot.html

HTH,
Ozgur
-------------- next part --------------
An HTML attachment was scrubbed...
URL: 
http://www.haskell.org/pipermail/beginners/attachments/20101126/877a92ee/attachment-0001.html

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

Message: 2
Date: Fri, 26 Nov 2010 21:51:19 +0100
From: J?rgen Doser <[email protected]>
Subject: Re: [Haskell-beginners] Re: Beginners Digest, Vol 29, Issue
        41
To: [email protected]
Message-ID: <[email protected]>
Content-Type: text/plain; charset="UTF-8"

El vie, 26-11-2010 a las 11:50 -0800, Russ Abbott escribió:
> [...]
> It's not accessing the data that concerns me, it's updating other
> elements.
> 
> Imagine a system that tracks student registration.
> 
> data Student = Student { sName :: String
>                                  {- ,  ... Other stuff -}
>                                  , coursesTaking :: [Course]
>                                  }
> 
> *
> data Rank = Assist | Assoc | Full
> 
> data Instructor = Student { iName :: String
>                                     , rank :: Rank               --
> defined,
> say, as   data Rank = Assist | Assoc | Full
> 
> {- ,  ... Other stuff -}
>                                     , coursesTeaching :: [Course]
>                                     }
> 
> data Course = Course { cName :: String
>                                , units :: Int
> 
>                               {- ,  ... Other stuff -}
>                                , instructor :: Instructor
>                                , enrolledStudents :: [Student]
>                                }
> 

In general, I try to avoid having cyclical data structures like this
(students pointing to courses, and courses pointing to students).
Functional programming languages are great for working with trees, not
necessarily general graphs. So I would try to find an acyclical
data-model for your problem. Your data-structure design looks more like
an object-oriented data-model. Basically, try to find the most
convenient "spanning tree" lying under your data-model. So you could
either remove the link from courses to students, or the link from
students to courses (or even both), and put the linking information
somewhere else, for example, into the Database structure you propose
below. Similar to what you would do if you would normalize an ER-schema
or SQL-schema for a Database.

>  Suppose I want a function that drops a student from a class.
> 
> drop :: (Course,  Student) ->
>  (Course,  Student)
> 
> I'm declaring the function as pair to pair because both change.
> 
> If I run drop, I get a new Course record and a new Student record.
> Doesn't
> that mean I have to change all the Student, Instructor, and Course
>  record
> s that refer to them, which also means I have to change all the Course
>  record
>  s that refer to them, etc.?
> 
> Wouldn't I have to write something like this?
> 
> data DataBase = DB { students    :: [Student]
>                             , instructors :: [Instructor]
>                             , courses     :: [Course]
>                             }
> 
I would probably do sth like this:

data DataBase = DB { students    :: [Student] -- Student has no coursesTaking 
field
                   , instructors :: [Instructor]
                   , courses     :: [Course]  -- Course has no enrolledStudent 
field
                   , student_courses :: [(Student,Course)] 
                   -- or even: 
                   -- student_courses :: [(Student_id, Course_id)]
                   }

Now, functions like 

> drop :: DB -> Course -> Student -> DB
 
are trivial.

Basically, when you find that you want to "change the state of the
world", it is a good idea to have "the world" available as a function
argument. Like in the DataBase datastructure above. Once you have that,
you can then play around: 

- how can I make certain aspects easier available in my "world"?

- maybe Map Student_id [Course_id] is better than 
  [(Student_id, Course_id)] ?

- or the other way round?

- maybe using the State Monad make the code easier:
  drop :: Course -> Student -> State DB ()

The object-oriented thinking: "I have a Student object, so I want to ask
*it* which courses it is taking" doesn't work too well, that's true.
Instead, ask "the system", "the world", "the student-course registry",
etc. what courses a particular student is taking.


        Jürgen



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

Message: 3
Date: Fri, 26 Nov 2010 20:51:46 +0000
From: Stephen Tetley <[email protected]>
Subject: Re: [Haskell-beginners] Re: Beginners Digest, Vol 29, Issue
        41
Cc: [email protected]
Message-ID:
        <[email protected]>
Content-Type: text/plain; charset=ISO-8859-1

On 26 November 2010 20:21, Ozgur Akgun <[email protected]> wrote:

> Not monad, but 'knot's :)
> If you tie the knot while creating those values, then, you don't need to
> update the same information in several places.

Oooh, masochism.

I'd be prefer not to make the data cyclic in the first place. Cyclic
structures are painful in functional languages - if you need them
there are ways to do it[*] but I would try to design around them.

Here I'd make a separate data type for the relation between course
name and student name (or better yet UIDs), rather than embed the data
in Course and Student objects.


[*] There was a "zipper-graph" paper by Normay Ramsey and colleagues
at the ML workshop a few years ago with one way to do it.


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

Message: 4
Date: Fri, 26 Nov 2010 14:46:23 -0800
From: Russ Abbott <[email protected]>
Subject: Re: [Haskell-beginners] Re: Beginners Digest, Vol 29, Issue
        41
To: Ozgur Akgun <[email protected]>
Cc: [email protected]
Message-ID:
        <[email protected]>
Content-Type: text/plain; charset="iso-8859-1"

It looks like exactly what I'm searching for!  But I'm going to have to
think about it for a while in order to understand it.
*
-- Russ *


On Fri, Nov 26, 2010 at 12:21 PM, Ozgur Akgun <[email protected]> wrote:

>
> On 26 November 2010 19:50, Russ Abbott <[email protected]> wrote:
>
>> P.S. I'll admit that I've spent virtually no time with monads and would
>> prefer to avoid them if possible.
>>
>
> Not monad, but 'knot's :)
>
> If you tie the knot while creating those values, then, you don't need to
> update the same information in several places.
>
> Have a look: http://haskell.org/wikisnapshot/TyingTheKnot.html
>
> HTH,
> Ozgur
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: 
http://www.haskell.org/pipermail/beginners/attachments/20101126/fbe25cfc/attachment-0001.html

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

Message: 5
Date: Sat, 27 Nov 2010 10:22:39 +0100
From: Tim Baumgartner <[email protected]>
Subject: [Haskell-beginners] Learning StateT
To: [email protected]
Message-ID:
        <[email protected]>
Content-Type: text/plain; charset=ISO-8859-1

Hi Haskellers,

in order to learn Monad Transformers, I'm trying to write a simple
calculator that has an internal state of type Calc that is modified
after each line the user enters. I started like this:

main :: IO ()
handleLine :: StateT Calc IO Bool   -- return's False in order to exit

main uses runState in order to extract the action it needs from the
transformer. Up to this point, it works fine. But in order to separate
I/O and parsing, I'd prefer to have another function

processInput :: String -> State Calc (Maybe String)

that updates the internal state and returns either a String that
should be printed or Nothing in order to exit. Trying to do that, I
didn't see another possibility but to call runState again from inside
handleLine. I guess there is a better solution. Can you help?

Thanks in advance
Tim


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

Message: 6
Date: Sat, 27 Nov 2010 08:35:30 -0500
From: Brent Yorgey <[email protected]>
Subject: Re: [Haskell-beginners] Learning StateT
To: [email protected]
Message-ID: <[email protected]>
Content-Type: text/plain; charset=us-ascii

On Sat, Nov 27, 2010 at 10:22:39AM +0100, Tim Baumgartner wrote:
> Hi Haskellers,
> 
> in order to learn Monad Transformers, I'm trying to write a simple
> calculator that has an internal state of type Calc that is modified
> after each line the user enters. I started like this:
> 
> main :: IO ()
> handleLine :: StateT Calc IO Bool   -- return's False in order to exit
> 
> main uses runState in order to extract the action it needs from the
> transformer. Up to this point, it works fine. But in order to separate
> I/O and parsing, I'd prefer to have another function
> 
> processInput :: String -> State Calc (Maybe String)

Separating I/O and parsing like this is a great idea.  Unfortunately,
from an engineering point of view, making different monad stacks work
together can be difficult (as you have discovered).  As I see it you
have three options (in increasing order of both desirability and
difficulty):

  1) Give up separating I/O and parsing, and just rewrite processInput
     to be in the StateT Calc IO monad.

  2) Write an adapter function

       withoutIO :: State s a -> StateT s IO a

     which makes a State s computation into a StateT s IO computation
     (which is guaranteed to do no I/O).  The implementation of
     withoutIO will probably involve runState, but it will be able to
     properly thread the state through from previous computations.

  3) Rewrite everything in a "capabilities" style, e.g. instead of the
     concrete type  StateT Calc IO Bool you would have

       (MonadState Calc m, MonadIO m) => m Bool

     and so on, which allows for much easier mixing of different
     concrete monad stacks.

Ultimately, (3) seems to me like the "right" way to do this sort of
thing.  It definitely runs into limitations as well, although many of
them can be mitigated by technology found in the Monatron package
(which unfortunately at the moment is woefully undocumented).

-Brent


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

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


End of Beginners Digest, Vol 29, Issue 43
*****************************************

Reply via email to