Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating instances of Abstract Data Types that recursively contain each other

Given two date types defined as follows:

data Foo = Foo Bar String
data Bar = Bar Foo String

How can I make foo and bar such that foo is Foo bar "foo" and bar is Bar foo "bar"?

What about when we change the types to:

data Foo = Foo Bar (MVar String)
data Bar = Bar Foo (MVar String)
like image 520
John F. Miller Avatar asked Apr 27 '11 21:04

John F. Miller


People also ask

Which abstract data type is best for simulating recursion?

The abstract data type ImList , and its two concrete classes Empty and Cons , form a recursive data type.

What is a recursive data type?

In computer programming languages, a recursive data type (also known as a recursively-defined, inductively-defined or inductive data type) is a data type for values that may contain other values of the same type. Data of recursive types are usually viewed as directed graphs.

Which type of data structure is used in recursion?

Linear Recursion in Data Structure It is the most widely used recursion method. applying this approach, functions call themselves in a non-complex form and then terminate the function with a termination condition.

What is recursion in data structure with example?

What is Recursion in Data Structure? A recursive data structure can be defined as a data structure that can be broken down into simple or miniature instances of itself. For example, trees are composed of small trees and leaf nodes while lists can contain smaller lists as their elements.


2 Answers

Just using a let is enough (let in Haskell is letrec, and supports mutually recursive definitions). The mutually recursive definitions set up cycles in the heap, that look like this:

foo bar let rec

The MVar initialization doesn't really change things in any meaningful way.

import Control.Concurrent

data Foo = Foo Bar (MVar String)

data Bar = Bar Foo (MVar String)

main = do
    a <- newMVar "foo"
    b <- newMVar "bar"

    let foo = Foo bar a
        bar = Bar foo b

    return ()
like image 150
Don Stewart Avatar answered Oct 04 '22 20:10

Don Stewart


Don answered the question as asked, but a slightly more interesting question is how to deal with

data Foo = Foo (MVar Bar) String
data Bar = Bar (MVar Foo) String

Now the two MVars aren't mere bystanders to the recursion, they are accomplices.

This can be done two ways:

1.) Either by doing what you would do in an imperative language like C:

mutation = do
   -- setting up an empty mvar
   bar <- newEmptyMVar
   foo <- newMVar (Foo bar "foo")
   -- and then filling it in
   putMVar bar (Bar foo "foo")
   return (foo, bar)

2.) or by using DoRec (formerly RecursiveDo) and mfix and behind the scenes tie the knot:

{-# LANGUAGE DoRec #-} 

mutual = do
   rec foo <- newMVar (Foo bar "foo")
       bar <- newMVar (Bar foo "foo")
   return (foo, bar)

This translates to something analogous to:

mutual = do
   (foo, bar) <- mfix $ \(foo, bar) -> do
       foo <- newMVar (Foo bar "foo")
       bar <- newMVar (Bar foo "foo")
       return (foo, bar)
  return (foo, bar)
like image 40
Edward Kmett Avatar answered Oct 04 '22 19:10

Edward Kmett