0

I was wondering if it is possible to have mutually recursive objects in Javascript and, if so, how?

Goal:

I want to have three objects:

  1. One that represents Boolean type with two values True and False
  2. One that represents a True object of Boolean type
  3. One that represents a False object of Boolean type

The trick is that I want to ask the True object its type and I should get back Boolean object and I want to ask a Boolean object its values and I should get back 2 objects: the True object and the False object.

But it should be totally be mutually recursive in the sense that I get something like this (though it doesn't necessarily have to be exactly like this):

True 
// {name : "True", type : [Object object]}

False
// {name : "False", type : [Object object]}

Boolean 
// {name : "Boolean", values : [Object object]}

Boolean.values  
// {True: [Object object], False: [Object object]}

True.type
// {name : "Boolean", values : [Object object]}

False.type
// {name : "Boolean", values : [Object object]}

Boolean.values.True 
// {name : "True", type: [Object object]}

Boolean.values.True.type 
// {name : "Boolean", values : [Object object]}

Boolean.values.True.type.values 
// {True : [Object object], False: [Object object]}

and so on...

If it helps, they should satisfy the properties that:

Boolean === Boolean.values.True.type 
Boolean === Boolean.values.True.type.values.True.type

True === Boolean.values.True
True === True.type.values.True.type.values.True.type.values.True

False === Boolean.values.False
False === True.type.values.False

and the ability to do this should be infinite

Note

These could be functions instead of objects. And the calls don't have to be exactly like this.

Community
  • 1
  • 1
Hassan Hayat
  • 1,056
  • 8
  • 20

2 Answers2

2

Here you go:

//Define the top level objects but avoid recursion
var True = {};
var False = {};
var Boolean = {
    values: {
        True: True,
        False: False
    }
};

//Create the recursion
True.type = Boolean;
False.type = Boolean;
Zenorbi
  • 2,504
  • 2
  • 15
  • 18
  • Thanks! Omg, I cannot believe how simple that was. I was scratching my head the whole time trying to figure it out (trying to create temporary variables and stuff...). Wow! Thanks – Hassan Hayat Nov 14 '14 at 18:00
  • So, if I get this straight, you can have to create the recursion later, you can't bound stuff to ```this``` or try to get the recursion from within the definition? – Hassan Hayat Nov 14 '14 at 18:01
  • 1
    @TheSeamau5: The problem is that `this` isn't really `this`. http://stackoverflow.com/questions/3127429/javascript-this-keyword What you call recursion is actually a cyclic reference. To create a cyclic reference, both objects need to know the other one in advance, hence we do the cyclic referencing **after** both objects are created. – Zenorbi Nov 14 '14 at 18:07
  • Oooooh I see. So, it's a good thing we can add properties to an object after it has been created. – Hassan Hayat Nov 14 '14 at 18:12
  • 1
    @TheSeamau5 No need to add fields later. You can init the True and False this way: `var True = {type: null}; var False = {type: null};` so you can define the fields in advance and assign a value to them later. – Zenorbi Nov 14 '14 at 18:15
1

That's very simple:

var Boolean = {
    name: "Boolean",
    values: {
        True: {
            name: "True"
        },
        False: {
            name: "False"
        }
    }
};

var True = Boolean.values.True;

var False = Boolean.values.False;

True.type = Boolean;

False.type = Boolean;

Are you trying to create an algebraic data type?


Edit: This is how I would create an algebraic data type:

function data(constructors) {
    var factory = function (constructor) {
        this.constructor = constructor || this;
    };

    var type = factory.prototype = {};

    for (var name in constructors) {
        var fields = constructors[name];

        if (fields) {
            var body = ["    var data = new " + name + "(arguments.callee);"];
            var length = fields.length;
            var params = [];

            for (var i = 0; i < length; i++) {
                var param = "arg" + i;
                body.push("    data." + fields[i] + " = " + param + ";");
                params.push(param);
            }

            body.unshift("return function (" + params.join(", ") + ") {");
            body.push("    return data;", "};");

            type[name] = Function(name, body.join("\n"))(factory);
        } else type[name] = new factory;
    }

    return type;
}

Using the data function we can define algebraic data types as follows:

var Boolean = data({
    True: null,
    False: null
});

var True = Boolean.True;
var False = Boolean.False;

var List = data({
    Nil: null,
    Cons: ["head", "tail"]
});

var Nil = List.Nil;
var Cons = List.Cons;

It has the following invariants:

Object.getPrototypeOf(True) === Boolean;
Object.getPrototypeOf(False) === Boolean;

Object.getPrototypeOf(Nil) === List;
Object.getPrototypeOf(Cons(0, Nil)) === List;

True.constructor === True;
False.constructor === False;

Nil.constructor === Nil;
Cons(0, Nil).constructor === Cons;

Using this, you can create pure functions as follows:

List.map = function (f) {
    switch (this.constructor) {
    case Nil: return Nil;
    case Cons:
        var x = this.head;
        var xs = this.tail;
        return Cons(f(x), xs.map(f));
    }
};

function map(f, a) {
    return a.map(f);
}

You can use it as follows:

function toList(a) {
    var list = Nil;
    for (var i = a.length - 1; i >= 0; i--) list = Cons(a[i], list);
    return list;
}

var xs = toList([1,2,3]);

var ys = map(function (a) {
    return a * 2;
}, xs);

Hope that helps.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • Yes I am! :) Thanks. I'm trying to make my own little programming language and I'm trying to support Algebraic Data Types. – Hassan Hayat Nov 14 '14 at 17:59
  • 1
    I updated my answer showing how you can easily create algebraic data types, and use them. Hope that helps. – Aadit M Shah Nov 14 '14 at 18:48
  • Oh wow @AaditMShah! Thanks! I already got them to work (although, my way was not so involved). Basically what I have is a method to construct an Object that has a name and a type. Then I have a method to construct a ```Type``` object with type Object and then I have a method to construct a ```Type Union``` which is a type with some name (a parameter) and values (a list of objects). So, to make a boolean, I just call the type union constructor with "Boolean" as name and two objects as values where each are objects create by the object constructor with "True" and "False" as names respectively. – Hassan Hayat Nov 14 '14 at 19:44