In FP, how do you establish relationships?

In FP, where there is no volatile state, and each operation returns a new state of the world. Given: I have a contact list and an individual contact.

I am adding Dirk to my address book. Dirk is the child of my address book. My address book is Dirk's parent. Being that I cannot set both links at the same time, I have a dilemma. Parent-child relationships should define an endless cycle in which I can forever from parent to parent to parent child.

Using JavaScript syntax:

var addresses = new AddressBook(); var dirk = new Contact(addresses, 'Dirk', ...); 

In the second line, I go through the address book without Dirk. Dirk has a parent link to the address book without him.

I suspect an answer, but I want to be sure. Am I really going to change the state to properly configure it, or is there some kind of technique that I'm missing?

+4
source share
3 answers

If you want this work to work as it would in your JavaScript example (so that you can directly look up the actual address book in real children), you need to change the address book. This is not even due to the initial creation of the parent and child elements (which can be controlled in some functional languages ​​more easily than in others), but because if you then add additional links to the address book, old entries will still retain obsolete versions of the address books.

In Clojure, it is tempting to use Atom or Ref to store the entire address book in that case, and then also place Atom or Ref pointing to the address book in each child element, but Clojure reference types that are only really designed to store immutable data and their nesting can lead to problems.

The best solution is to provide symbolic names to your entities (keywords, numbers, UUIDs are all right) and store them on a map somewhere. Using one atom might look like this:

 (def state (atom {:contacts {:dirk ...} :address-books {}})) 

Then you can add Dirk to the new address book (creating it along the path as a hash map) as follows:

 (swap! state (fn [state-map] (update-in state-map [:address-book :my-address-book] (fn [abook] (let [entries (get abook :entries [])] (assoc abook :entries (conj entries :dirk))))))) 

Please note that this adds Dirk to the address book as a symbolic link ( :dirk ) to search the top-level status map under the :contacts key on the map. If you also want the Dirk contact to maintain the list of address books of which it is a member, also use update-in , adding the corresponding information to the Dirk contact, possibly removing some nesting with -> :

 (-> state-map (update-in [...] ...) (update-in [...] ...)) 
+3
source

you can do this with an altered state and without it with the same idea. You run a function that takes the initial state of the addresses, and the state you want to add, which then returns a new set of addresses, including the new state. He does this without destroying the original, of course, because someone can look at it.

determine the base address book:

 user> (def addresses []) #'user/addresses 

Define a new address book containing the new value:

 user> (def book-with-dirk (conj addresses {:name "dirk" :address "123 internet st."})) #'user/book-with-dirk user> book-with-dirk [{:name "dirk", :address "123 internet st."}] 

which does not change the base address book, but creates a new address book, which effectively combines the original address book with the new value for dirk. Thus, the addresses remain the same.

 user> addresses [] 

You can also use managed mutable state to maintain the contents of identity names with names in a functional order. the original value in the address atom still exists if someone is looking at it (and otherwise is GCd)

 user> (def addresses (atom [])) #'user/addresses 

create a new address book, which, as above, includes dirk, in addition, the following value is also created in the address identifier:

 user> (def book-with-dirk (swap! addresses conj {:name "dirk" :address "123 internet st."})) #'user/book-with-dirk 

now book-with-dirk is the value that contains the book with dirk in it.

 user> book-with-dirk [{:name "dirk", :address "123 internet st."}] 

and the address also contains the new meaning.

 user> @addresses [{:name "dirk", :address "123 internet st."}] 

If I then add Joe, book-with-dirk will not change

 user> (swap! addresses conj {:name "Joe" :address "321 internet st."}) [{:name "dirk", :address "123 internet st."} {:name "Joe", :address "321 internet st."}] user> book-with-dirk [{:name "dirk", :address "123 internet st."}] 
+2
source

Since you mentioned FP in general, I would add another view - lazy rating . I am not very good at JS and Clojure, so I will give examples in different languages, but it may also be possible to use this idea.

Many functional languages ​​have the concept of lazy rating . This means that the value is calculated only at the point at which it is really needed. Naturally, such lazy calculations should be referentially transparent (should not depend on external information, should be free of variable state and side effects, etc.), because we never know when (or if at all) they will be evaluated .

For example, in Haskell, all calculations are lazy, so we can write simply

 let address = Address contact {- other fields -} contact = Contact address {- other fields -} in {- some expression that uses address and contact -} 

Or we can make a list whose tail is the list itself. The result is an endless list with a repeating element that takes up only a constant amount of memory

 infList :: a -> [a] infList x = l where l = x : l 

For more information, see Binding a Node in the Haskell Wiki.

If the language does not have a lazy rating, you can implement it yourself: if the value has not yet been requested, calculate it, save and return. Next time, just return what was previously calculated. Of course, for this you will need volatility, but the volatile state is hidden inside the software component, and if the calculation is referentially transparent, volatility never leaks out.

+2
source

Source: https://habr.com/ru/post/1491225/


All Articles