
Brent Yorgey wrote
Someone did respond to your question, with a link to the data-accessor package on Hackage (you could also take a look at fclabels or lenses). Do those help address your issue?
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] } 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] } drop :: DB -> Course -> Student -> DB But even if I do that, doesn't that require a lot of code to get everything updated? Is there a simple way to write that function? In a previous message I guessed that the best way to do it is with Maps, e.g. Map Student [Course] etc. I'd very much appreciate seeing the recommended code for the drop function. Thanks. * *-- Russ* P.S. I'll admit that I've spent virtually no time with monads and would prefer to avoid them if possible.

On 26 November 2010 19:50, Russ Abbott
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

On 26 November 2010 20:21, Ozgur Akgun
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.

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
On 26 November 2010 19:50, Russ Abbott
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

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
participants (4)
-
Jürgen Doser
-
Ozgur Akgun
-
Russ Abbott
-
Stephen Tetley